diff --git a/.bumpversion.cfg b/.bumpversion.cfg
new file mode 100644
index 0000000..07a7c38
--- /dev/null
+++ b/.bumpversion.cfg
@@ -0,0 +1,20 @@
+[bumpversion]
+current_version = 3.1.2
+commit = False
+tag = False
+
+[bumpversion:file:src/graphql/version.py]
+search = version = "{current_version}"
+replace = version = "{new_version}"
+
+[bumpversion:file:docs/conf.py]
+search = version = release = '{current_version}'
+replace = version = release = '{new_version}'
+
+[bumpversion:file:README.md]
+search = The current version {current_version}
+replace = The current version {new_version}
+
+[bumpversion:file:pyproject.toml]
+search = version = "{current_version}"
+replace = version = "{new_version}"
diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..cd976b1
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,21 @@
+[run]
+branch = True
+source = src
+omit =
+    */conftest.py
+    */cached_property.py
+    */is_collection.py
+    */test_*_fuzz.py
+
+[report]
+exclude_lines =
+    pragma: no cover
+    raise NotImplementedError
+    raise TypeError\(f?"Unexpected
+    assert False,
+    \s+next\($
+    if MYPY:
+    if TYPE_CHECKING:
+    ^\s+\.\.\.$
+    ^\s+pass$
+ignore_errors = True
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..d4a2c44
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,21 @@
+# http://editorconfig.org
+
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+trim_trailing_whitespace = true
+insert_final_newline = true
+charset = utf-8
+end_of_line = lf
+
+[*.bat]
+indent_style = tab
+end_of_line = crlf
+
+[LICENSE]
+insert_final_newline = false
+
+[Makefile]
+indent_style = tab
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..ccded58
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,4 @@
+[flake8]
+ignore = E203,W503
+exclude = .git,.mypy_cache,.pytest_cache,.tox,.venv,__pycache__,build,dist,docs
+max-line-length = 88
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000..3ba13e0
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1 @@
+blank_issues_enabled: false
diff --git a/.github/ISSUE_TEMPLATE/open-a-graphql-core-issue.md b/.github/ISSUE_TEMPLATE/open-a-graphql-core-issue.md
new file mode 100644
index 0000000..a6fc342
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/open-a-graphql-core-issue.md
@@ -0,0 +1,26 @@
+---
+name: Open a GraphQL-core issue
+about: General template for all GraphQL-core issues
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+# Questions regarding how to use GraphQL
+
+If you have a question on how to use GraphQL, please [post it to Stack Overflow](https://stackoverflow.com/questions/ask?tags=graphql) with the tag [#graphql](https://stackoverflow.com/questions/tagged/graphql).
+
+# Reporting issues with GraphQL-core 3
+
+Before filing a new issue, make sure an issue for your problem doesn't already exist and that this is not an issue that should be filed against a different repository (see below).
+
+The best way to get a bug fixed is to provide a _pull request_ with a simplified failing test case (or better yet, include a fix).
+
+# Reporting issues with GraphQL-core 2
+
+Please use the issue tracker of the [legacy repository](https://github.com/graphql-python/graphql-core-legacy) for issues with legacy versions of GraphQL-core.
+
+# Feature requests
+
+GraphQL-core is a Python port of the [GraphQL.js](https://github.com/graphql/graphql-js) reference implementation of the [GraphQL specification](https://github.com/graphql/graphql-spec). To discuss new features which are not Python specific, please open an issue against the GraphQL.js project. To discuss features that fundamentally change the way GraphQL works, open an issue against the specification.
diff --git a/.gitignore b/.gitignore
index b0ba7d2..6b51313 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,64 +1,27 @@
-# Compile Python files
-__pycache__/
-*.py[cod]
-
-# Distribution / packaging
-.Python
-venv
-.venv
-env
-.env
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-*.egg-info/
-.installed.cfg
-*.egg
 
-# PyInstaller
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
+.cache/
 .coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
+.env*/
+.idea/
+.mypy_cache/
 .pytest_cache/
+.tox/
+.venv*/
+.vs/
 
-# PyBuilder
-target/
-
-# Type checking
-/.mypy_cache
-.pyre
-/type_info.json
-
-# Sphinx documentation
+build/
+dist/
 docs/_build/
+htmlcov/
+pip-wheel-metadata/
+wheels/
 
-# IntelliJ
-.idea
-*.iml
+play/
 
-# Visual Studio
-/.vscode
+__pycache__/
 
-# OS X
-.DS_Store
+*.cover
+*.egg
+*.egg-info
+*.log
+*.py[cod]
diff --git a/.mypy.ini b/.mypy.ini
new file mode 100644
index 0000000..0daa396
--- /dev/null
+++ b/.mypy.ini
@@ -0,0 +1,20 @@
+[mypy]
+python_version = 3.8
+check_untyped_defs = True
+no_implicit_optional = True
+strict_optional = True
+warn_redundant_casts = True
+warn_unused_ignores = True
+disallow_untyped_defs = True
+
+[mypy-graphql.pyutils.frozen_dict]
+disallow_untyped_defs = False
+
+[mypy-graphql.pyutils.frozen_list]
+disallow_untyped_defs = False
+
+[mypy-graphql.type.introspection]
+disallow_untyped_defs = False
+
+[mypy-tests.*]
+disallow_untyped_defs = False
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
deleted file mode 100644
index 172fc3b..0000000
--- a/.pre-commit-config.yaml
+++ /dev/null
@@ -1,29 +0,0 @@
-repos:
--   repo: git://github.com/pre-commit/pre-commit-hooks
-    rev: v2.1.0
-    hooks:
-    -   id: check-merge-conflict
-    -   id: check-json
-    -   id: check-yaml
-    -   id: debug-statements
-    -   id: end-of-file-fixer
-        exclude: ^docs/.*$
-    -   id: pretty-format-json
-        args:
-        - --autofix
-    -   id: trailing-whitespace
-        exclude: README.md
--   repo: https://github.com/asottile/pyupgrade
-    rev: v1.12.0
-    hooks:
-    -   id: pyupgrade
--   repo: https://github.com/ambv/black
-    rev: 19.3b0
-    hooks:
-    - id: black
-      language_version: python3.6
--   repo: https://github.com/pycqa/flake8
-    rev: 3.7.7
-    hooks:
-    -   id: flake8
-        exclude: test_.*$
diff --git a/.travis.yml b/.travis.yml
index d565f32..8be142f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,36 +1,37 @@
 language: python
 matrix:
   include:
-    -  env: TOXENV=py27
-       python: 2.7
-    -  env: TOXENV=py34
-       python: 3.4
-    -  env: TOXENV=py35
-       python: 3.5
-    -  env: TOXENV=py36
-       python: 3.6
-    -  env: TOXENV=py37
-       python: 3.7
-       dist: xenial
-       sudo: true # required workaround for https://github.com/travis-ci/travis-ci/issues/9815
-    -  env: TOXENV=pypy
-       python: pypy-5.7.1
-    -  env: TOXENV=pre-commit
-       python: 3.6
-    -  env: TOXENV=mypy
-       python: 3.6
-install: pip install coveralls tox
-script: tox
-after_success: coveralls
+    - name: Code quality tests
+      env: TOXENV=black,flake8,mypy,docs,manifest
+      python: 3.8
+    - name: Unit tests with Python 3.8
+      env: TOXENV=py38
+      python: 3.8
+    - name: Unit tests with Python 3.7
+      env: TOXENV=py37
+      python: 3.7
+    - name: Unit tests with Python 3.6
+      env: TOXENV=py36
+      python: 3.6
 cache:
   directories:
-    - $HOME/.cache/pip
-    - $HOME/.cache/pre-commit
+    - "$HOME/.cache/pip"
+    - "$TRAVIS_BUILD_DIR/.tox"
+install:
+  - pip install "poetry>=1,<2"
+  - poetry install
+script:
+  - tox -e $TOXENV
+after_success:
+  - codecov
 deploy:
   provider: pypi
-  user: syrusakbary
+  distributions: sdist bdist_wheel
   on:
+    branch: master
     tags: true
+    python: 3.8
+  skip_existing: true
+  user: mvanlonden
   password:
-    secure: q7kMxnJQ5LWr8fxVbQPm3pAXKRfYa1d2defM1UXKTQ+Gi6ZQ+QEOAOSbX1SKzYH62+hNRY2JGTeLkTQBeEYn05GJRh+WOkFzIFV1EnsgFbimSb6B83EmM57099GjJnO2nRUU4jyuNGU1joTeaD/g08ede072Es1I7DTuholNbYIq+brL/LQMJycuqZMoWUW4+pP8dE9SmjThMNYHlqNhzdXSE3BlZU0xcw7F2Ea384DNcekIIcapZuPjL167VouuSH/oMQMxBJo+ExEHdbqn5zsA9xcoF931XCgz4ag8U3jHhE48ZXM/xwdQt+S8JnOZcuv3MoAAioMbh+bYXUt2lmENWXCKK1kMDz2bJymwEUeZLA6lFxJQwvlVShowdi7xeyDYLIbeF7yG90Hd+5BqCZn5imzlcQxpjanaQq6xLwAzo6AHssWtd5bBOjDydknPxd1t3QGDoDvtfRdqrfOhlVX5813Hmd/vAopBAba7msKPMLxhsqDZKkwsVrLJLJDjGdpHNl/bbVaMsYcPrsFxa2W8PuddQFviHbL4HDNqHn5SpRwJcQ18YL1X5StQnUz1J+4E0W4mLrU3YW1k8RGlKTes/GeTH4sU+Sh3I9vrDv7849A8U9sSFyB2PT4Jyy8O2R5UyjoqnZDrkYYbLdn/caVo3ThrubTpwdPBmNwcDLA=
-  distributions: "sdist bdist_wheel"
+    secure: BjKsrn6rww8EEevU2VspVOFqrX/O09Yh8Xbjg+m6/yHmKDsX7kh+U3smgtOXGneKvnu6pLqXJS+gqGDi/u8OJfa8zrYyLlOXMEjPg0kWMl+w+s8E6BtEbOJ1qghS00kqk8xQeMK1YcfJLzztHt4g1VQM0C/y1kMub+Q8nz6KWHifgiSxcud7G/y8TrSYHcm74XWiY0U1zStUfx4z9Zftjr4MRFG9igXXLYCvGYoHOo0Ji4/6Ssgr3lX7GMT7glgBgrv5DmFpCWkVSuHrdyA7x32+O2XU8hLtGGPVoRWFhxc3cQ/QR4VrzCDauZMMOQG3c8dHoXz2T3dgsf9vN6XAB2HiaC1G2E3sI9C44hlUWxl3/5arZ0P4pq1MTRZaX2P6jU+cgyzWyuJ6f3bM2sYo0xOswhG1uP85iVWa2NlJIxmL8NMSoHlDtRLGWxMdqA6Vprn/Jn93AOkywa810ZCuSbgJEHS4ukJYVqE13mmwhL6Ocy9dZDvYGpYro05MlzDUvA8nN5HPuGdXYDRJFMfmBAkMlO0zWd+AtPx3CydFcs2bI5gKmANCfA5fdUot+xWAndMjJB89W0lCgl9b5N9LMxGL//erRbn7PDANB7xNcBrcDaV857lvUJsf2x5P0CE0NOHgEMW7SXZe6rPQZ+9fB+KEhN2RAFG/iIxRlNa+5kM=
diff --git a/CODEOWNERS b/CODEOWNERS
index 9a73b37..9a4d3d1 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1 +1 @@
-/ @syrusakbary @ekampf @dan98765 @projectcheshire @cito
+* @Cito
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 141776c..5012202 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,8 @@
-The MIT License (MIT)
+MIT License
 
-Copyright (c) 2016 GraphQL Python
+Copyright (c) GraphQL Contributors (GraphQL.js)
+Copyright (c) Syrus Akbary (GraphQL-core 2)
+Copyright (c) Christoph Zwerschke (GraphQL-core 3)
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/MANIFEST.in b/MANIFEST.in
index b912b3d..c1e8c0f 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1,5 +1,24 @@
-global-exclude tests/*
-recursive-exclude tests *
-recursive-exclude tests_py35 *
+include MANIFEST.in
+
+include CODEOWNERS
 include LICENSE
 include README.md
+
+include .bumpversion.cfg
+include .coveragerc
+include .editorconfig
+include .flake8
+include .mypy.ini
+
+include codecov.yml
+include tox.ini
+
+include poetry.lock
+include pyproject.toml
+
+graft src/graphql
+graft tests
+recursive-include docs *.txt *.rst conf.py Makefile make.bat *.jpg *.png *.gif
+prune docs/_build
+
+global-exclude *.py[co] __pycache__
diff --git a/README.md b/README.md
index 0234d66..b6900ae 100644
--- a/README.md
+++ b/README.md
@@ -1,159 +1,249 @@
-# GraphQL-core
+# GraphQL-core 3
 
-GraphQL for Python.
-
-_This library is a port of [graphql-js](https://github.com/graphql/graphql-js) to Python and currently is up-to-date with release [0.6.0](https://github.com/graphql/graphql-js/releases/tag/v0.6.0)._
+GraphQL-core 3 is a Python 3.6+ port of [GraphQL.js](https://github.com/graphql/graphql-js),
+the JavaScript reference implementation for [GraphQL](https://graphql.org/),
+a query language for APIs created by Facebook.
 
 [![PyPI version](https://badge.fury.io/py/graphql-core.svg)](https://badge.fury.io/py/graphql-core)
-[![Build Status](https://travis-ci.org/graphql-python/graphql-core.svg?branch=master)](https://travis-ci.org/graphql-python/graphql-core)
-[![Coverage Status](https://coveralls.io/repos/graphql-python/graphql-core/badge.svg?branch=master&service=github)](https://coveralls.io/github/graphql-python/graphql-core?branch=master)
-[![Public Slack Discussion](https://graphql-slack.herokuapp.com/badge.svg)](https://graphql-slack.herokuapp.com/)
+[![Documentation Status](https://readthedocs.org/projects/graphql-core-3/badge/)](https://graphql-core-3.readthedocs.io)
+[![Build Status](https://travis-ci.com/graphql-python/graphql-core.svg?branch=master)](https://travis-ci.com/graphql-python/graphql-core)
+[![Coverage Status](https://codecov.io/gh/graphql-python/graphql-core/branch/master/graph/badge.svg)](https://codecov.io/gh/graphql-python/graphql-core)
+[![Dependency Updates](https://pyup.io/repos/github/graphql-python/graphql-core/shield.svg)](https://pyup.io/repos/github/graphql-python/graphql-core/)
+[![Python 3 Status](https://pyup.io/repos/github/graphql-python/graphql-core/python-3-shield.svg)](https://pyup.io/repos/github/graphql-python/graphql-core/)
+[![Code Style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
 
-See more complete documentation at http://graphql.org/ and
-http://graphql.org/docs/api-reference-graphql/.
+The current version 3.1.2 of GraphQL-core is up-to-date with GraphQL.js version 15.1.0.
 
-For questions, ask [Stack Overflow](http://stackoverflow.com/questions/tagged/graphql).
+An extensive test suite with over 2200 unit tests and 100% coverage comprises a
+replication of the complete test suite of GraphQL.js, making sure this port is
+reliable and compatible with GraphQL.js.
 
-## Getting Started
 
-An overview of the GraphQL language is available in the
-[README](https://github.com/facebook/graphql/blob/master/README.md) for the
-[Specification for GraphQL](https://github.com/facebook/graphql).
+## Documentation
 
-The overview describes a simple set of GraphQL examples that exist as [tests](https://github.com/graphql-python/graphql-core/tree/master/tests/)
-in this repository. A good way to get started is to walk through that README and the corresponding tests
-in parallel.
+A more detailed documentation for GraphQL-core 3 can be found at
+[graphql-core-3.readthedocs.io](https://graphql-core-3.readthedocs.io/).
 
-### Using graphql-core
+The documentation for GraphQL.js can be found at [graphql.org/graphql-js/](https://graphql.org/graphql-js/).
 
-Install from pip:
+The documentation for GraphQL itself can be found at [graphql.org](https://graphql.org/).
 
-```sh
-pip install graphql-core
-```
+There will be also [blog articles](https://cito.github.io/tags/graphql/) with more usage
+examples.
+
+
+## Getting started
+
+An overview of GraphQL in general is available in the
+[README](https://github.com/graphql/graphql-spec/blob/master/README.md) for the
+[Specification for GraphQL](https://github.com/graphql/graphql-spec). That overview
+describes a simple set of GraphQL examples that exist as [tests](tests) in this
+repository. A good way to get started with this repository is to walk through that
+README and the corresponding tests in parallel.
+
+
+## Installation
+
+GraphQL-core 3 can be installed from PyPI using the built-in pip command:
+
+    python -m pip install "graphql-core>=3"
+
+Alternatively, you can also use [pipenv](https://docs.pipenv.org/) for installation in a
+virtual environment:
 
-GraphQL.js provides two important capabilities: building a type schema, and
+    pipenv install "graphql-core>=3"
+
+
+## Usage
+
+GraphQL-core provides two important capabilities: building a type schema, and
 serving queries against that type schema.
 
-First, build a GraphQL type schema which maps to your code base.
+First, build a GraphQL type schema which maps to your code base:
 
 ```python
 from graphql import (
-    graphql,
-    GraphQLSchema,
-    GraphQLObjectType,
-    GraphQLField,
-    GraphQLString
-)
+    GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
 
 schema = GraphQLSchema(
-  query= GraphQLObjectType(
-    name='RootQueryType',
-    fields={
-      'hello': GraphQLField(
-        type= GraphQLString,
-        resolver=lambda *_: 'world'
-      )
-    }
-  )
-)
+    query=GraphQLObjectType(
+        name='RootQueryType',
+        fields={
+            'hello': GraphQLField(
+                GraphQLString,
+                resolve=lambda obj, info: 'world')
+        }))
 ```
 
-This defines a simple schema with one type and one field, that resolves
-to a fixed value. The `resolve` function can return a value, a promise,
-or an array of promises. A more complex example is included in the top
-level [tests](https://github.com/graphql-python/graphql-core/tree/master/tests/) directory.
+This defines a simple schema with one type and one field, that resolves to a fixed
+value. The `resolve` function can return a value, a co-routine object or a list of
+these. It takes two positional arguments; the first one provides the root or the
+resolved parent field, the second one provides a `GraphQLResolveInfo` object which
+contains information about the execution state of the query, including a `context`
+attribute holding per-request state such as authentication information or database
+session. Any GraphQL arguments are passed to the `resolve` functions as individual
+keyword arguments.
+
+Note that the signature of the resolver functions is a bit different in GraphQL.js,
+where the context is passed separately and arguments are passed as a single object.
+Also note that GraphQL fields must be passed as a `GraphQLField` object explicitly.
+Similarly, GraphQL arguments must be passed as `GraphQLArgument` objects.
+
+A more complex example is included in the top level [tests](tests) directory.
 
 Then, serve the result of a query against that type schema.
 
 ```python
+from graphql import graphql_sync
+
 query = '{ hello }'
 
-result = graphql(schema, query)
+print(graphql_sync(schema, query))
+```
 
-# Prints
-# {'hello': 'world'} (as OrderedDict)
+This runs a query fetching the one field defined, and then prints the result:
 
-print result.data
+```python
+ExecutionResult(data={'hello': 'world'}, errors=None)
 ```
 
-This runs a query fetching the one field defined. The `graphql` function will
-first ensure the query is syntactically and semantically valid before executing
-it, reporting errors otherwise.
+The `graphql_sync` function will first ensure the query is syntactically and
+semantically valid before executing it, reporting errors otherwise.
 
 ```python
-query = '{ boyhowdy }'
+from graphql import graphql_sync
 
-result = graphql(schema, query)
+query = '{ BoyHowdy }'
 
-# Prints
-# [GraphQLError('Cannot query field "boyhowdy" on type "RootQueryType".',)]
-
-print result.errors
+print(graphql_sync(schema, query))
 ```
 
-### Executors
+Because we queried a non-existing field, we will get the following result:
 
-The graphql query is executed, by default, synchronously (using `SyncExecutor`).
-However the following executors are available if we want to resolve our fields in parallel:
+```python
+ExecutionResult(data=None, errors=[GraphQLError(
+    "Cannot query field 'BoyHowdy' on type 'RootQueryType'.",
+    locations=[SourceLocation(line=1, column=3)])])
+```
 
-- `graphql.execution.executors.asyncio.AsyncioExecutor`: This executor executes the resolvers in the Python asyncio event loop.
-- `graphql.execution.executors.gevent.GeventExecutor`: This executor executes the resolvers in the Gevent event loop.
-- `graphql.execution.executors.process.ProcessExecutor`: This executor executes each resolver as a process.
-- `graphql.execution.executors.thread.ThreadExecutor`: This executor executes each resolver in a Thread.
-- `graphql.execution.executors.sync.SyncExecutor`: This executor executes each resolver synchronusly (default).
+The `graphql_sync` function assumes that all resolvers return values synchronously. By
+using coroutines as resolvers, you can also create results in an asynchronous fashion
+with the `graphql` function.
 
-#### Usage
+```python
+import asyncio
+from graphql import (
+    graphql, GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
 
-You can specify the executor to use via the executor keyword argument in the `grapqhl.execution.execute` function.
 
-```python
-from graphql.execution.execute import execute
+async def resolve_hello(obj, info):
+    await asyncio.sleep(3)
+    return 'world'
 
-execute(schema, ast, executor=SyncExecutor())
+schema = GraphQLSchema(
+    query=GraphQLObjectType(
+        name='RootQueryType',
+        fields={
+            'hello': GraphQLField(
+                GraphQLString,
+                resolve=resolve_hello)
+        }))
+
+
+async def main():
+    query = '{ hello }'
+    print('Fetching the result...')
+    result = await graphql(schema, query)
+    print(result)
+
+
+loop = asyncio.get_event_loop()
+try:
+    loop.run_until_complete(main())
+finally:
+    loop.close()
 ```
 
-### Contributing
 
-After cloning this repo, create a [virtualenv](https://virtualenv.pypa.io/en/stable/) and ensure dependencies are installed by running:
+## Goals and restrictions
 
-```sh
-virtualenv venv
-source venv/bin/activate
-pip install -e ".[test]"
-```
+GraphQL-core tries to reproduce the code of the reference implementation GraphQL.js
+in Python as closely as possible and to stay up-to-date with the latest development of
+GraphQL.js.
 
-Well-written tests and maintaining good test coverage is important to this project. While developing, run new and existing tests with:
+GraphQL-core 3 (formerly known as GraphQL-core-next) has been created as a modern
+alternative to [GraphQL-core 2](https://github.com/graphql-python/graphql-core-legacy),
+a prior work by Syrus Akbary, based on an older version of GraphQL.js and also
+targeting older Python versions. Some parts of GraphQL-core 3 have been inspired by
+GraphQL-core 2 or directly taken over with only slight modifications, but most of the
+code has been re-implemented from scratch, replicating the latest code in GraphQL.js
+very closely and adding type hints for Python.
 
-```sh
-py.test PATH/TO/MY/DIR/test_test.py # Single file
-py.test PATH/TO/MY/DIR/ # All tests in directory
-```
+Design goals for the GraphQL-core 3 library are:
 
-Add the `-s` flag if you have introduced breakpoints into the code for debugging.
-Add the `-v` ("verbose") flag to get more detailed test output. For even more detailed output, use `-vv`.
-Check out the [pytest documentation](https://docs.pytest.org/en/latest/) for more options and test running controls.
+* to be a simple, cruft-free, state-of-the-art implementation of GraphQL using current
+  library and language versions
+* to be very close to the GraphQL.js reference implementation, while still using a
+  Pythonic API and code style
+* to make extensive use of Python type hints, similar to how GraphQL.js makes uses Flow
+* to use [black](https://github.com/ambv/black) for automatic code formatting
+* to replicate the complete Mocha-based test suite of GraphQL.js using
+  [pytest](https://docs.pytest.org/)
 
-GraphQL-core supports several versions of Python. To make sure that changes do not break compatibility with any of those versions, we use `tox` to create virtualenvs for each python version and run tests with that version. To run against all python versions defined in the `tox.ini` config file, just run:
+Some restrictions (mostly in line with the design goals):
 
-```sh
-tox
-```
+* requires Python 3.6 or newer
+* does not support some already deprecated methods and options of GraphQL.js
+* supports asynchronous operations only via async.io
+  (does not support the additional executors in GraphQL-core)
 
-If you wish to run against a specific version defined in the `tox.ini` file:
 
-```sh
-tox -e py36
-```
+## Integration with other libraries and roadmap
+
+* [Graphene](http://graphene-python.org/) is a more high-level framework for building
+  GraphQL APIs in Python, and there is already a whole ecosystem of libraries, server
+  integrations and tools built on top of Graphene. Most of this Graphene ecosystem has
+  also been created by Syrus Akbary, who meanwhile has handed over the maintenance
+  and future development to members of the GraphQL-Python community.
+
+  The current version 2 of Graphene is using Graphql-core 2 as core library for much of
+  the heavy lifting. Note that Graphene 2 is not compatible with GraphQL-core 3.
+  The  new version 3 of Graphene will use GraphQL-core 3 instead of GraphQL-core 2.
+
+* [Ariadne](https://github.com/mirumee/ariadne) is a Python library for implementing
+  GraphQL servers using schema-first approach created by Mirumee Software.
+
+  Ariadne is already using GraphQL-core 3 as its GraphQL implementation.
+
+* [Strawberry](https://github.com/strawberry-graphql/strawberry), created by Patrick
+  Arminio, is a new GraphQL library for Python 3, inspired by dataclasses,
+  that is also using GraphQL-core 3 as underpinning.
+
+
+## Changelog
+
+Changes are tracked as
+[GitHub releases](https://github.com/graphql-python/graphql-core/releases).
+
+
+## Credits and history
 
-Tox can only use whatever versions of python are installed on your system. When you create a pull request, Travis will also be running the same tests and report the results, so there is no need for potential contributors to try to install every single version of python on their own system ahead of time. We appreciate opening issues and pull requests to make GraphQL-core even more stable & useful!
+The GraphQL-core 3 library
+* has been created and is maintained by Christoph Zwerschke
+* uses ideas and code from GraphQL-core 2, a prior work by Syrus Akbary
+* is a Python port of GraphQL.js which has been developed by Lee Byron and others
+  at Facebook, Inc. and is now maintained
+  by the [GraphQL foundation](https://gql.foundation/join/)
 
-## Main Contributors
+Please watch the recording of Lee Byron's short keynote on the
+[history of GraphQL](https://www.youtube.com/watch?v=VjHWkBr3tjI)
+at the open source leadership summit 2019 to better understand
+how and why GraphQL was created at Facebook and then became open sourced
+and ported to many different programming languages.
 
-- [@syrusakbary](https://github.com/syrusakbary/)
-- [@jhgg](https://github.com/jhgg/)
-- [@dittos](https://github.com/dittos/)
 
 ## License
 
-[MIT License](https://github.com/graphql-python/graphql-core/blob/master/LICENSE)
+GraphQL-core 3 is
+[MIT-licensed](https://github.com/graphql-python/graphql-core/blob/master/LICENSE),
+just like GraphQL.js.
diff --git a/bin/autolinter b/bin/autolinter
deleted file mode 100755
index cd4ef37..0000000
--- a/bin/autolinter
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/bash
-
-# Install the required scripts with
-# pip install autoflake autopep8 isort
-autoflake ./graphql/ ./tests/ -r --remove-unused-variables --in-place
-autopep8 ./tests/ ./graphql/ -r --in-place --experimental --aggressive --max-line-length 120
-isort -rc ./tests/ ./graphql/
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 0000000..c393a12
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,10 @@
+codecov:
+  notify:
+    require_ci_to_pass: yes
+
+comment: no
+coverage:
+  status:
+    project:
+      default:
+        target: auto
\ No newline at end of file
diff --git a/conftest.py b/conftest.py
deleted file mode 100644
index 6b91637..0000000
--- a/conftest.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Configuration for pytest to automatically collect types.
-# Thanks to Guilherme Salgado.
-import pytest
-
-try:
-    import pyannotate_runtime
-
-    PYANOTATE_PRESENT = True
-except ImportError:
-    PYANOTATE_PRESENT = False
-
-if PYANOTATE_PRESENT:
-
-    def pytest_collection_finish(session):
-        """Handle the pytest collection finish hook: configure pyannotate.
-        Explicitly delay importing `collect_types` until all tests have
-        been collected.  This gives gevent a chance to monkey patch the
-        world before importing pyannotate.
-        """
-        from pyannotate_runtime import collect_types
-
-        collect_types.init_types_collection()
-
-    @pytest.fixture(autouse=True)
-    def collect_types_fixture():
-        from pyannotate_runtime import collect_types
-
-        collect_types.resume()
-        yield
-        collect_types.pause()
-
-    def pytest_sessionfinish(session, exitstatus):
-        from pyannotate_runtime import collect_types
-
-        collect_types.dump_stats("type_info.json")
diff --git a/docs/Makefile b/docs/Makefile
index 1d14ddd..d4bb2cb 100644
--- a/docs/Makefile
+++ b/docs/Makefile
@@ -1,192 +1,20 @@
-# Makefile for Sphinx documentation
+# Minimal makefile for Sphinx documentation
 #
 
-# You can set these variables from the command line.
-SPHINXOPTS    =
-SPHINXBUILD   = sphinx-build
-PAPER         =
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
 BUILDDIR      = _build
 
-# User-friendly check for sphinx-build
-ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1)
-$(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/)
-endif
-
-# Internal variables.
-PAPEROPT_a4     = -D latex_paper_size=a4
-PAPEROPT_letter = -D latex_paper_size=letter
-ALLSPHINXOPTS   = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-# the i18n builder cannot share the environment and doctrees with the others
-I18NSPHINXOPTS  = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
-
-.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest coverage gettext
-
+# Put it first so that "make" without argument is like "make help".
 help:
-	@echo "Please use \`make <target>' where <target> is one of"
-	@echo "  html       to make standalone HTML files"
-	@echo "  dirhtml    to make HTML files named index.html in directories"
-	@echo "  singlehtml to make a single large HTML file"
-	@echo "  pickle     to make pickle files"
-	@echo "  json       to make JSON files"
-	@echo "  htmlhelp   to make HTML files and a HTML help project"
-	@echo "  qthelp     to make HTML files and a qthelp project"
-	@echo "  applehelp  to make an Apple Help Book"
-	@echo "  devhelp    to make HTML files and a Devhelp project"
-	@echo "  epub       to make an epub"
-	@echo "  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
-	@echo "  latexpdf   to make LaTeX files and run them through pdflatex"
-	@echo "  latexpdfja to make LaTeX files and run them through platex/dvipdfmx"
-	@echo "  text       to make text files"
-	@echo "  man        to make manual pages"
-	@echo "  texinfo    to make Texinfo files"
-	@echo "  info       to make Texinfo files and run them through makeinfo"
-	@echo "  gettext    to make PO message catalogs"
-	@echo "  changes    to make an overview of all changed/added/deprecated items"
-	@echo "  xml        to make Docutils-native XML files"
-	@echo "  pseudoxml  to make pseudoxml-XML files for display purposes"
-	@echo "  linkcheck  to check all external links for integrity"
-	@echo "  doctest    to run all doctests embedded in the documentation (if enabled)"
-	@echo "  coverage   to run coverage check of the documentation (if enabled)"
-
-clean:
-	rm -rf $(BUILDDIR)/*
-
-html:
-	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
-
-dirhtml:
-	$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
-	@echo
-	@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
-
-singlehtml:
-	$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
-	@echo
-	@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
-
-pickle:
-	$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
-	@echo
-	@echo "Build finished; now you can process the pickle files."
-
-json:
-	$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
-	@echo
-	@echo "Build finished; now you can process the JSON files."
-
-htmlhelp:
-	$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
-	@echo
-	@echo "Build finished; now you can run HTML Help Workshop with the" \
-	      ".hhp project file in $(BUILDDIR)/htmlhelp."
-
-qthelp:
-	$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
-	@echo
-	@echo "Build finished; now you can run "qcollectiongenerator" with the" \
-	      ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
-	@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/graphql-py.qhcp"
-	@echo "To view the help file:"
-	@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/graphql-py.qhc"
-
-applehelp:
-	$(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp
-	@echo
-	@echo "Build finished. The help book is in $(BUILDDIR)/applehelp."
-	@echo "N.B. You won't be able to view it unless you put it in" \
-	      "~/Library/Documentation/Help or install it in your application" \
-	      "bundle."
-
-devhelp:
-	$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
-	@echo
-	@echo "Build finished."
-	@echo "To view the help file:"
-	@echo "# mkdir -p $$HOME/.local/share/devhelp/graphql-py"
-	@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/graphql-py"
-	@echo "# devhelp"
-
-epub:
-	$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
-	@echo
-	@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
-
-latex:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo
-	@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
-	@echo "Run \`make' in that directory to run these through (pdf)latex" \
-	      "(use \`make latexpdf' here to do that automatically)."
-
-latexpdf:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through pdflatex..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-latexpdfja:
-	$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
-	@echo "Running LaTeX files through platex and dvipdfmx..."
-	$(MAKE) -C $(BUILDDIR)/latex all-pdf-ja
-	@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
-
-text:
-	$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
-	@echo
-	@echo "Build finished. The text files are in $(BUILDDIR)/text."
-
-man:
-	$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
-	@echo
-	@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
-
-texinfo:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo
-	@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
-	@echo "Run \`make' in that directory to run these through makeinfo" \
-	      "(use \`make info' here to do that automatically)."
-
-info:
-	$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
-	@echo "Running Texinfo files through makeinfo..."
-	make -C $(BUILDDIR)/texinfo info
-	@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
-
-gettext:
-	$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
-	@echo
-	@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
-
-changes:
-	$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
-	@echo
-	@echo "The overview file is in $(BUILDDIR)/changes."
-
-linkcheck:
-	$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
-	@echo
-	@echo "Link check complete; look for any errors in the above output " \
-	      "or in $(BUILDDIR)/linkcheck/output.txt."
-
-doctest:
-	$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
-	@echo "Testing of doctests in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/doctest/output.txt."
-
-coverage:
-	$(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage
-	@echo "Testing of coverage in the sources finished, look at the " \
-	      "results in $(BUILDDIR)/coverage/python.txt."
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
 
-xml:
-	$(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml
-	@echo
-	@echo "Build finished. The XML files are in $(BUILDDIR)/xml."
+.PHONY: help Makefile
 
-pseudoxml:
-	$(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml
-	@echo
-	@echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml."
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/conf.py b/docs/conf.py
index 0e34c36..0d198e6 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,7 +1,7 @@
 # -*- coding: utf-8 -*-
 #
-# graphqllib documentation build configuration file, created by
-# sphinx-quickstart on Wed Sep 16 20:08:39 2015.
+# GraphQL-core 3 documentation build configuration file, created by
+# sphinx-quickstart on Thu Jun 21 16:28:30 2018.
 #
 # This file is execfile()d with the current directory set to its
 # containing dir.
@@ -12,57 +12,56 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys
-import os
-
 # If extensions (or modules to document with autodoc) are in another directory,
 # add these directories to sys.path here. If the directory is relative to the
 # documentation root, use os.path.abspath to make it absolute, like shown here.
-sys.path.insert(0, os.path.abspath(".."))
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
 
 # -- General configuration ------------------------------------------------
 
 # If your documentation needs a minimal Sphinx version, state it here.
+#
 # needs_sphinx = '1.0'
 
 # Add any Sphinx extension module names here, as strings. They can be
 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
 # ones.
 extensions = [
-    "sphinx.ext.autodoc",
-    "sphinx.ext.intersphinx",
-    "sphinx.ext.todo",
-    "sphinx.ext.coverage",
-    "sphinx.ext.viewcode",
+    'sphinx.ext.autodoc',
 ]
 
 # Add any paths that contain templates here, relative to this directory.
-templates_path = ["_templates"]
+templates_path = ['_templates']
 
 # The suffix(es) of source filenames.
 # You can specify multiple suffix as a list of string:
+#
 # source_suffix = ['.rst', '.md']
-source_suffix = ".rst"
+source_suffix = '.rst'
 
 # The encoding of source files.
+#
 # source_encoding = 'utf-8-sig'
 
 # The master toctree document.
-master_doc = "index"
+master_doc = 'index'
 
 # General information about the project.
-project = u"graphqllib"
-copyright = u"2015, Taeho Kim"
-author = u"Taeho Kim"
+project = 'GraphQL-core 3'
+copyright = '2020, Christoph Zwerschke'
+author = 'Christoph Zwerschke'
 
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
 #
 # The short X.Y version.
-version = "0.1"
+# version = '3.1'
 # The full version, including alpha/beta/rc tags.
-release = "0.1a0"
+version = release = '3.1.2'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
@@ -73,31 +72,85 @@ language = None
 
 # There are two options for replacing |today|: either, you set today to some
 # non-false value, then it is used:
+#
 # today = ''
+#
 # Else, today_fmt is used as the format for a strftime call.
+#
 # today_fmt = '%B %d, %Y'
 
 # List of patterns, relative to source directory, that match files and
 # directories to ignore when looking for source files.
-exclude_patterns = ["_build"]
+# This patterns also effect to html_static_path and html_extra_path
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+# AutoDoc configuration
+#autoclass_content = "both"
+autodoc_default_options = {
+    'members': True,
+    'inherited-members': True,
+    'private-members': False,
+    'special-members': '__init__',
+    'undoc-members': True,
+    'show-inheritance': True
+}
+autosummary_generate = True
+
+# ignore certain warnings
+# (references to some of the Python built-in types do not resolve correctly)
+nitpicky = True
+nitpick_ignore = [('py:class', t) for t in (
+    'dict', 'list', 'object', 'tuple',
+    'Exception', 'TypeError', 'ValueError',
+    'builtins.str', 'enum.Enum',
+    'typing.Callable', 'typing.Dict', 'typing.Generic', 'typing.List',
+    'graphql.pyutils.cached_property.CachedProperty',
+    'graphql.pyutils.path.Path',
+    'graphql.error.graphql_error.GraphQLError',
+    'graphql.language.ast.DefinitionNode',
+    'graphql.language.ast.ExecutableDefinitionNode',
+    'graphql.language.ast.Node',
+    'graphql.language.ast.ScalarTypeDefinitionNode',
+    'graphql.language.ast.SelectionNode',
+    'graphql.language.ast.TypeDefinitionNode',
+    'graphql.language.ast.TypeExtensionNode',
+    'graphql.language.ast.TypeNode',
+    'graphql.language.ast.TypeSystemDefinitionNode',
+    'graphql.language.ast.ValueNode',
+    'graphql.language.visitor.Visitor',
+    'graphql.type.definition.GraphQLNamedType',
+    'graphql.type.definition.GraphQLType',
+    'graphql.type.definition.GraphQLWrappingType',
+    'graphql.validation.rules.ASTValidationRule',
+    'graphql.validation.rules.SDLValidationRule',
+    'graphql.validation.rules.ValidationRule',
+    'graphql.validation.validation_context.ASTValidationContext',
+    'graphql.validation.rules.known_argument_names'
+    '.KnownArgumentNamesOnDirectivesRule',
+    'graphql.validation.rules.provided_required_arguments'
+    '.ProvidedRequiredArgumentsOnDirectivesRule')]
 
 # The reST default role (used for this markup: `text`) to use for all
 # documents.
+#
 # default_role = None
 
 # If true, '()' will be appended to :func: etc. cross-reference text.
+#
 # add_function_parentheses = True
 
 # If true, the current module name will be prepended to all description
 # unit titles (such as .. function::).
+#
 # add_module_names = True
 
 # If true, sectionauthor and moduleauthor directives will be shown in the
 # output. They are ignored by default.
+#
 # show_authors = False
 
 # The name of the Pygments (syntax highlighting) style to use.
-pygments_style = "sphinx"
+pygments_style = 'sphinx'
 
 # A list of ignored prefixes for module index sorting.
 # modindex_common_prefix = []
@@ -106,37 +159,42 @@ pygments_style = "sphinx"
 # keep_warnings = False
 
 # If true, `todo` and `todoList` produce output, else they produce nothing.
-todo_include_todos = True
-
+todo_include_todos = False
 
 # -- Options for HTML output ----------------------------------------------
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = "alabaster"
+#
+html_theme = 'sphinx_rtd_theme'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
 # documentation.
+#
 # html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
 # html_theme_path = []
 
-# The name for this set of Sphinx documents.  If None, it defaults to
-# "<project> v<release> documentation".
-# html_title = None
+# The name for this set of Sphinx documents.
+# "<project> v<release> documentation" by default.
+#
+# html_title = 'GraphQL-core v3.1.0'
 
 # A shorter title for the navigation bar.  Default is the same as html_title.
+#
 # html_short_title = None
 
 # The name of an image file (relative to this directory) to place at the top
 # of the sidebar.
+#
 # html_logo = None
 
-# The name of an image file (within the static path) to use as favicon of the
-# docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
 # pixels large.
+#
 # html_favicon = None
 
 # Add any paths that contain custom static files (such as style sheets) here,
@@ -147,44 +205,57 @@ html_theme = "alabaster"
 # Add any extra paths that contain custom files (such as robots.txt or
 # .htaccess) here, relative to this directory. These files are copied
 # directly to the root of the documentation.
+#
 # html_extra_path = []
 
-# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
-# using the given strftime format.
-# html_last_updated_fmt = '%b %d, %Y'
+# If not None, a 'Last updated on:' timestamp is inserted at every page
+# bottom, using the given strftime format.
+# The empty string is equivalent to '%b %d, %Y'.
+#
+# html_last_updated_fmt = None
 
 # If true, SmartyPants will be used to convert quotes and dashes to
 # typographically correct entities.
+#
 # html_use_smartypants = True
 
 # Custom sidebar templates, maps document names to template names.
+#
 # html_sidebars = {}
 
 # Additional templates that should be rendered to pages, maps page names to
 # template names.
+#
 # html_additional_pages = {}
 
 # If false, no module index is generated.
+#
 # html_domain_indices = True
 
 # If false, no index is generated.
+#
 # html_use_index = True
 
 # If true, the index is split into individual pages for each letter.
+#
 # html_split_index = False
 
 # If true, links to the reST sources are added to the pages.
-# html_show_sourcelink = True
+#
+html_show_sourcelink = False
 
 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
+#
 # html_show_sphinx = True
 
 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
+#
 # html_show_copyright = True
 
 # If true, an OpenSearch description file will be output, and all pages will
 # contain a <link> tag referring to it.  The value of this option must be the
 # base URL from which the finished HTML is served.
+#
 # html_use_opensearch = ''
 
 # This is the file name suffix for HTML files (e.g. ".xhtml").
@@ -193,58 +264,76 @@ html_theme = "alabaster"
 # Language to be used for generating the HTML full-text search index.
 # Sphinx supports the following languages:
 #   'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja'
-#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr'
+#   'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr', 'zh'
+#
 # html_search_language = 'en'
 
 # A dictionary with options for the search language support, empty by default.
-# Now only 'ja' uses this config value
+# 'ja' uses this config value.
+# 'zh' user can custom change `jieba` dictionary path.
+#
 # html_search_options = {'type': 'default'}
 
 # The name of a javascript file (relative to the configuration directory) that
 # implements a search results scorer. If empty, the default will be used.
+#
 # html_search_scorer = 'scorer.js'
 
 # Output file base name for HTML help builder.
-htmlhelp_basename = "graphqllibdoc"
+htmlhelp_basename = 'GraphQL-core-3-doc'
 
 # -- Options for LaTeX output ---------------------------------------------
 
 latex_elements = {
-    # The paper size ('letterpaper' or 'a4paper').
-    # 'papersize': 'letterpaper',
-    # The font size ('10pt', '11pt' or '12pt').
-    # 'pointsize': '10pt',
-    # Additional stuff for the LaTeX preamble.
-    # 'preamble': '',
-    # Latex figure (float) alignment
-    # 'figure_align': 'htbp',
+     # The paper size ('letterpaper' or 'a4paper').
+     #
+     # 'papersize': 'letterpaper',
+
+     # The font size ('10pt', '11pt' or '12pt').
+     #
+     # 'pointsize': '10pt',
+
+     # Additional stuff for the LaTeX preamble.
+     #
+     # 'preamble': '',
+
+     # Latex figure (float) alignment
+     #
+     # 'figure_align': 'htbp',
 }
 
 # Grouping the document tree into LaTeX files. List of tuples
 # (source start file, target name, title,
 #  author, documentclass [howto, manual, or own class]).
 latex_documents = [
-    (master_doc, "graphqllib.tex", u"graphqllib Documentation", u"Taeho Kim", "manual")
+    (master_doc, 'GraphQL-core-3.tex', 'GraphQL-core 3 Documentation',
+     'Christoph Zwerschke', 'manual'),
 ]
 
 # The name of an image file (relative to this directory) to place at the top of
 # the title page.
+#
 # latex_logo = None
 
 # For "manual" documents, if this is true, then toplevel headings are parts,
 # not chapters.
+#
 # latex_use_parts = False
 
 # If true, show page references after internal links.
+#
 # latex_show_pagerefs = False
 
 # If true, show URL addresses after external links.
+#
 # latex_show_urls = False
 
 # Documents to append as an appendix to all manuals.
+#
 # latex_appendices = []
 
 # If false, no module index is generated.
+#
 # latex_domain_indices = True
 
 
@@ -252,9 +341,13 @@ latex_documents = [
 
 # One entry per manual page. List of tuples
 # (source start file, name, description, authors, manual section).
-man_pages = [(master_doc, "graphqllib", u"graphqllib Documentation", [author], 1)]
+man_pages = [
+    (master_doc, 'graphql-core', 'GraphQL-core 3 Documentation',
+     [author], 1)
+]
 
 # If true, show URL addresses after external links.
+#
 # man_show_urls = False
 
 
@@ -264,29 +357,23 @@ man_pages = [(master_doc, "graphqllib", u"graphqllib Documentation", [author], 1
 # (source start file, target name, title, author,
 #  dir menu entry, description, category)
 texinfo_documents = [
-    (
-        master_doc,
-        "graphqllib",
-        u"graphqllib Documentation",
-        author,
-        "graphqllib",
-        "One line description of project.",
-        "Miscellaneous",
-    )
+    (master_doc, 'GraphQL-core', 'GraphQL-core 3 Documentation',
+     author, 'GraphQL-core 3', 'One line description of project.',
+     'Miscellaneous'),
 ]
 
 # Documents to append as an appendix to all manuals.
+#
 # texinfo_appendices = []
 
 # If false, no module index is generated.
+#
 # texinfo_domain_indices = True
 
 # How to display URL addresses: 'footnote', 'no', or 'inline'.
+#
 # texinfo_show_urls = 'footnote'
 
 # If true, do not generate a @detailmenu in the "Top" node's menu.
+#
 # texinfo_no_detailmenu = False
-
-
-# Example configuration for intersphinx: refer to the Python standard library.
-intersphinx_mapping = {"https://docs.python.org/": None}
diff --git a/docs/diffs.rst b/docs/diffs.rst
new file mode 100644
index 0000000..0bdae09
--- /dev/null
+++ b/docs/diffs.rst
@@ -0,0 +1,82 @@
+Differences from GraphQL.js
+===========================
+
+The goal of GraphQL-core 3 is to be a faithful replication of `GraphQL.js`_,  the JavaScript reference implementation for GraphQL, in Python 3, and to keep it aligned and up to date with the ongoing development of GraphQL.js. Therefore, we strive to be as compatible as possible to the original JavaScript library, sometimes at the cost of being less Pythonic than other libraries written particularly for Python. We also avoid incorporating additional features that do not exist in the JavaScript library, in order to keep the task of maintaining the Python code and keeping it in line with the JavaScript code manageable. The preferred way of getting new features into GraphQL-core is to propose and discuss them on the `GraphQL.js issue tracker`_ first, try to get them included into GraphQL.js, and from there ported to GraphQL-core.
+
+.. _GraphQL.js: https://github.com/graphql/graphql-js
+.. _GraphQL.js issue tracker: https://github.com/graphql/graphql-js/issues
+.. _Graphene: https://graphene-python.org/
+
+.. currentmodule:: graphql
+
+Having said this, in a few places we allowed the API to be a bit more Pythonic than the direct equivalent would have been. We also added a few features that do not exist in the JavaScript library, mostly to support existing higher level libraries such as Graphene_ and the different naming conventions in Python. The most notable differences are the following:
+
+
+Direct attribute access in GraphQL types
+----------------------------------------
+
+You can access
+
+* the **fields** of GraphQLObjectTypes, GraphQLInterfaceTypes and GraphQLInputObjectTypes,
+* the **interfaces** of GraphQLObjectTypes,
+* the **types** of GraphQLUnionTypes,
+* the **values** of GraphQLEnumTypes and
+* the **query**, **mutation**, **subscription** and **type_map** of GraphQLSchemas
+
+directly as attributes, instead of using getters.
+
+For example, to get the fields of a GraphQLObjectType ``obj``, you write ``obj.fields`` instead of ``obj.getFields()``.
+
+
+Arguments, fields and values are dictionaries
+---------------------------------------------
+
+* The **arguments** of GraphQLDirectives and GraphQLFields,
+* the **fields** of GraphQLObjectTypes, GraphQLInterfaceTypes and GraphQLInputObjectTypes, and
+* the **values** of GraphQLEnumTypes
+
+are always Python dictionaries in GraphQL-core, while they are returned as Arrays in GraphQL.js. Also, the values of these dictionaries do not have ``name`` attributes, since the names are already used as the keys of these dictionaries.
+
+
+Shorthand notation for creating GraphQL types
+---------------------------------------------
+
+The following shorthand notations are possible:
+
+* Where you need to pass a GraphQLArgumentMap, i.e. a dictionary with names as keys and GraphQLArguments as values, you can also pass GraphQLInputTypes as values. The GraphQLInputTypes are then automatically wrapped into GraphQLArguments.
+* Where you need to pass a GraphQLFieldMap, i.e. a dictionary with names as keys and GraphQLFields as values, you can also pass GraphQLOutputTypes as values. The GraphQLOutputTypes are then automatically wrapped into GraphQLFields.
+* Where you need to pass a GraphQLInputFieldMap, i.e. a dictionary with names as keys and GraphQLInputFields as values, you can also pass GraphQLInputTypes as values. The GraphQLInputTypes are then automatically wrapped into GraphQLInputFields.
+* Where you need to pass a GraphQLEnumValueMap, i.e. a dictionary with names as keys and GraphQLEnumValues as values, you can pass any other Python objects as values. These will be automatically wrapped into GraphQLEnumValues. You can also pass a Python Enum type as GraphQLEnumValueMap.
+
+
+Custom output names of arguments and input fields
+-------------------------------------------------
+
+You can pass a custom ``out_name`` argument to :class:`GraphQLArgument` and :class:`GraphQLInputField` that allows using JavaScript naming conventions (camelCase) on ingress and Python naming conventions (snake_case) on egress. This feature is used by Graphene.
+
+
+Custom output types of input object types
+-----------------------------------------
+
+You can also pass a custom ``out_type`` argument to :class:`GraphQLInputObjectType` that allows conversion to any Python type on egress instead of conversion to a dictionary, which is the default. This is used to support the container feature of Graphene InputObjectTypes.
+
+
+Custom middleware
+-----------------
+
+The :func:`execution.execute` function takes an additional ``middleware`` argument which must be a sequence of middleware functions or a :class:`MiddlewareManager` object. This feature is used by Graphene to affect the evaluation of fields using custom middleware. There has been a `request <https://github.com/graphql/graphql-js/issues/1516>`_ to add this to GraphQL.js as well, but so far this feature only exists in GraphQL-core.
+
+
+Custom execution context
+------------------------
+
+The :func:`execution.execute` function takes an additional ``execution_context_class`` argument which allows specifying a custom execution context class instead of the default :class:`ExecutionContext` used by GraphQL-core.
+
+
+Registering special types for descriptions
+------------------------------------------
+
+Normally, descriptions for GraphQL types must be strings. However, sometimes you may want to use other kinds of objects which are not strings, but are only resolved to strings at runtime. This is possible if you register the classes of such objects with :func:`pyutils.register_description`.
+
+
+If you notice any other important differences, please let us know so that they can be either removed or listed here.
diff --git a/docs/index.rst b/docs/index.rst
index 1a3b48b..81102c2 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -1,20 +1,20 @@
-.. graphql-py documentation master file, created by
-   sphinx-quickstart on Wed Sep 16 20:08:39 2015.
-   You can adapt this file completely to your liking, but it should at least
-   contain the root `toctree` directive.
+Welcome to GraphQL-core 3
+=========================
 
-Welcome to graphql-py's documentation!
-======================================
-
-Contents:
+Contents
+--------
 
 .. toctree::
    :maxdepth: 2
 
+   intro
+   usage/index
+   diffs
+   modules/graphql
 
 
 Indices and tables
-==================
+------------------
 
 * :ref:`genindex`
 * :ref:`modindex`
diff --git a/docs/intro.rst b/docs/intro.rst
new file mode 100644
index 0000000..3bd8383
--- /dev/null
+++ b/docs/intro.rst
@@ -0,0 +1,97 @@
+Introduction
+============
+
+`GraphQL-core-3`_ is a Python port of `GraphQL.js`_,
+the JavaScript reference implementation for GraphQL_,
+a query language for APIs created by Facebook.
+
+`GraphQL`_ consists of three parts:
+
+* A type system that you define
+* A query language that you use to query the API
+* An execution and validation engine
+
+The reference implementation closely follows the `Specification for GraphQL`_
+which consists of the following sections:
+
+* Language_
+* `Type System`_
+* Introspection_
+* Validation_
+* Execution_
+* Response_
+
+This division into subsections is reflected in the :ref:`sub-packages` of
+GraphQL-core 3. Each of these sub-packages implements the aspects specified in
+one of the sections of the specification.
+
+
+Getting started
+---------------
+
+You can install GraphQL-core 3 using pip_::
+
+    pip install "graphql-core>=3"
+
+You can also install GraphQL-core 3 with pipenv_, if you prefer that::
+
+    pipenv install "graphql-core>=3"
+
+Now you can start using GraphQL-core 3 by importing from the top-level
+:mod:`graphql` package. Nearly everything defined in the sub-packages
+can also be imported directly from the top-level package.
+
+.. currentmodule:: graphql
+
+For instance, using the types defined in the :mod:`graphql.type` package,
+you can define a GraphQL schema, like this simple one::
+
+    from graphql import (
+        GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString)
+
+    schema = GraphQLSchema(
+        query=GraphQLObjectType(
+            name='RootQueryType',
+            fields={
+                'hello': GraphQLField(
+                    GraphQLString,
+                    resolve=lambda obj, info: 'world')
+            }))
+
+The :mod:`graphql.execution` package implements the mechanism for executing
+GraphQL queries. The top-level :func:`graphql` and :func:`graphql_sync`
+functions also parse and validate queries using the :mod:`graphql.language`
+and :mod:`graphql.validation` modules.
+
+So to validate and execute a query against our simple schema, you can do::
+
+    from graphql import graphql_sync
+
+    query = '{ hello }'
+
+    print(graphql_sync(schema, query))
+
+This will yield the following output::
+
+    ExecutionResult(data={'hello': 'world'}, errors=None)
+
+
+Reporting Issues and Contributing
+---------------------------------
+
+Please visit the `GitHub repository of GraphQL-core 3`_ if you're interested
+in the current development or want to report issues or send pull requests.
+
+.. _GraphQL: https://graphql.org/
+.. _GraphQL.js: https://github.com/graphql/graphql-js
+.. _GraphQL-core-3: https://github.com/graphql-python/graphql-core
+.. _GitHub repository of GraphQL-core 3: https://github.com/graphql-python/graphql-core
+.. _Specification for GraphQL: https://facebook.github.io/graphql/
+.. _Language: https://facebook.github.io/graphql/draft/#sec-Language
+.. _Type System: https://facebook.github.io/graphql/draft/#sec-Type-System
+.. _Introspection: https://facebook.github.io/graphql/draft/#sec-Introspection
+.. _Validation: https://facebook.github.io/graphql/draft/#sec-Validation
+.. _Execution: https://facebook.github.io/graphql/draft/#sec-Execution
+.. _Response: https://facebook.github.io/graphql/draft/#sec-Response
+.. _pip: https://pip.pypa.io/
+.. _pipenv: https://github.com/pypa/pipenv
diff --git a/docs/make.bat b/docs/make.bat
index 578edde..922152e 100644
--- a/docs/make.bat
+++ b/docs/make.bat
@@ -1,263 +1,35 @@
-@ECHO OFF
-
-REM Command file for Sphinx documentation
-
-if "%SPHINXBUILD%" == "" (
-	set SPHINXBUILD=sphinx-build
-)
-set BUILDDIR=_build
-set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
-set I18NSPHINXOPTS=%SPHINXOPTS% .
-if NOT "%PAPER%" == "" (
-	set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
-	set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
-)
-
-if "%1" == "" goto help
-
-if "%1" == "help" (
-	:help
-	echo.Please use `make ^<target^>` where ^<target^> is one of
-	echo.  html       to make standalone HTML files
-	echo.  dirhtml    to make HTML files named index.html in directories
-	echo.  singlehtml to make a single large HTML file
-	echo.  pickle     to make pickle files
-	echo.  json       to make JSON files
-	echo.  htmlhelp   to make HTML files and a HTML help project
-	echo.  qthelp     to make HTML files and a qthelp project
-	echo.  devhelp    to make HTML files and a Devhelp project
-	echo.  epub       to make an epub
-	echo.  latex      to make LaTeX files, you can set PAPER=a4 or PAPER=letter
-	echo.  text       to make text files
-	echo.  man        to make manual pages
-	echo.  texinfo    to make Texinfo files
-	echo.  gettext    to make PO message catalogs
-	echo.  changes    to make an overview over all changed/added/deprecated items
-	echo.  xml        to make Docutils-native XML files
-	echo.  pseudoxml  to make pseudoxml-XML files for display purposes
-	echo.  linkcheck  to check all external links for integrity
-	echo.  doctest    to run all doctests embedded in the documentation if enabled
-	echo.  coverage   to run coverage check of the documentation if enabled
-	goto end
-)
-
-if "%1" == "clean" (
-	for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
-	del /q /s %BUILDDIR%\*
-	goto end
-)
-
-
-REM Check if sphinx-build is available and fallback to Python version if any
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 goto sphinx_python
-goto sphinx_ok
-
-:sphinx_python
-
-set SPHINXBUILD=python -m sphinx.__init__
-%SPHINXBUILD% 2> nul
-if errorlevel 9009 (
-	echo.
-	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
-	echo.installed, then set the SPHINXBUILD environment variable to point
-	echo.to the full path of the 'sphinx-build' executable. Alternatively you
-	echo.may add the Sphinx directory to PATH.
-	echo.
-	echo.If you don't have Sphinx installed, grab it from
-	echo.http://sphinx-doc.org/
-	exit /b 1
-)
-
-:sphinx_ok
-
-
-if "%1" == "html" (
-	%SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/html.
-	goto end
-)
-
-if "%1" == "dirhtml" (
-	%SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
-	goto end
-)
-
-if "%1" == "singlehtml" (
-	%SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
-	goto end
-)
-
-if "%1" == "pickle" (
-	%SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can process the pickle files.
-	goto end
-)
-
-if "%1" == "json" (
-	%SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can process the JSON files.
-	goto end
-)
-
-if "%1" == "htmlhelp" (
-	%SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can run HTML Help Workshop with the ^
-.hhp project file in %BUILDDIR%/htmlhelp.
-	goto end
-)
-
-if "%1" == "qthelp" (
-	%SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; now you can run "qcollectiongenerator" with the ^
-.qhcp project file in %BUILDDIR%/qthelp, like this:
-	echo.^> qcollectiongenerator %BUILDDIR%\qthelp\graphql-py.qhcp
-	echo.To view the help file:
-	echo.^> assistant -collectionFile %BUILDDIR%\qthelp\graphql-py.ghc
-	goto end
-)
-
-if "%1" == "devhelp" (
-	%SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished.
-	goto end
-)
-
-if "%1" == "epub" (
-	%SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The epub file is in %BUILDDIR%/epub.
-	goto end
-)
-
-if "%1" == "latex" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "latexpdf" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	cd %BUILDDIR%/latex
-	make all-pdf
-	cd %~dp0
-	echo.
-	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "latexpdfja" (
-	%SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
-	cd %BUILDDIR%/latex
-	make all-pdf-ja
-	cd %~dp0
-	echo.
-	echo.Build finished; the PDF files are in %BUILDDIR%/latex.
-	goto end
-)
-
-if "%1" == "text" (
-	%SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The text files are in %BUILDDIR%/text.
-	goto end
-)
-
-if "%1" == "man" (
-	%SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The manual pages are in %BUILDDIR%/man.
-	goto end
-)
-
-if "%1" == "texinfo" (
-	%SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
-	goto end
-)
-
-if "%1" == "gettext" (
-	%SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
-	goto end
-)
-
-if "%1" == "changes" (
-	%SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.The overview file is in %BUILDDIR%/changes.
-	goto end
-)
-
-if "%1" == "linkcheck" (
-	%SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Link check complete; look for any errors in the above output ^
-or in %BUILDDIR%/linkcheck/output.txt.
-	goto end
-)
-
-if "%1" == "doctest" (
-	%SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Testing of doctests in the sources finished, look at the ^
-results in %BUILDDIR%/doctest/output.txt.
-	goto end
-)
-
-if "%1" == "coverage" (
-	%SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Testing of coverage in the sources finished, look at the ^
-results in %BUILDDIR%/coverage/python.txt.
-	goto end
-)
-
-if "%1" == "xml" (
-	%SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The XML files are in %BUILDDIR%/xml.
-	goto end
-)
-
-if "%1" == "pseudoxml" (
-	%SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml
-	if errorlevel 1 exit /b 1
-	echo.
-	echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml.
-	goto end
-)
-
-:end
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/modules/error.rst b/docs/modules/error.rst
new file mode 100644
index 0000000..ef51316
--- /dev/null
+++ b/docs/modules/error.rst
@@ -0,0 +1,15 @@
+Error
+=====
+
+.. currentmodule:: graphql.error
+
+.. automodule:: graphql.error
+
+.. autoexception:: GraphQLError
+   :members:
+
+.. autoexception:: GraphQLSyntaxError
+
+.. autofunction:: format_error
+.. autofunction:: located_error
+.. autofunction:: print_error
diff --git a/docs/modules/execution.rst b/docs/modules/execution.rst
new file mode 100644
index 0000000..6565b0b
--- /dev/null
+++ b/docs/modules/execution.rst
@@ -0,0 +1,15 @@
+Execution
+=========
+
+.. currentmodule:: graphql.execution
+
+.. automodule:: graphql.execution
+
+.. autofunction:: execute
+.. autofunction:: default_field_resolver
+
+.. autoclass:: ExecutionContext
+.. autoclass:: ExecutionResult
+
+.. autofunction:: get_directive_values
+
diff --git a/docs/modules/graphql.rst b/docs/modules/graphql.rst
new file mode 100644
index 0000000..2109e44
--- /dev/null
+++ b/docs/modules/graphql.rst
@@ -0,0 +1,31 @@
+Reference
+=========
+
+.. currentmodule:: graphql
+
+.. automodule:: graphql
+
+.. _top-level-functions:
+
+Top-Level Functions
+-------------------
+
+.. autofunction:: graphql
+.. autofunction:: graphql_sync
+
+.. _sub-packages:
+
+Sub-Packages
+------------
+
+.. toctree::
+   :maxdepth: 1
+
+   error
+   execution
+   language
+   pyutils
+   subscription
+   type
+   utilities
+   validation
diff --git a/docs/modules/language.rst b/docs/modules/language.rst
new file mode 100644
index 0000000..ef649d6
--- /dev/null
+++ b/docs/modules/language.rst
@@ -0,0 +1,123 @@
+Language
+========
+
+.. automodule:: graphql.language
+
+AST
+---
+
+.. autoclass:: Location
+.. autoclass:: Node
+
+Each kind of AST node has its own class:
+
+.. autoclass:: ArgumentNode
+.. autoclass:: BooleanValueNode
+.. autoclass:: DefinitionNode
+.. autoclass:: DirectiveDefinitionNode
+.. autoclass:: DirectiveNode
+.. autoclass:: DocumentNode
+.. autoclass:: EnumTypeDefinitionNode
+.. autoclass:: EnumTypeExtensionNode
+.. autoclass:: EnumValueDefinitionNode
+.. autoclass:: EnumValueNode
+.. autoclass:: ExecutableDefinitionNode
+.. autoclass:: FieldDefinitionNode
+.. autoclass:: FieldNode
+.. autoclass:: FloatValueNode
+.. autoclass:: FragmentDefinitionNode
+.. autoclass:: FragmentSpreadNode
+.. autoclass:: InlineFragmentNode
+.. autoclass:: InputObjectTypeDefinitionNode
+.. autoclass:: InputObjectTypeExtensionNode
+.. autoclass:: InputValueDefinitionNode
+.. autoclass:: IntValueNode
+.. autoclass:: InterfaceTypeDefinitionNode
+.. autoclass:: InterfaceTypeExtensionNode
+.. autoclass:: ListTypeNode
+.. autoclass:: ListValueNode
+.. autoclass:: NameNode
+.. autoclass:: NamedTypeNode
+.. autoclass:: NonNullTypeNode
+.. autoclass:: NullValueNode
+.. autoclass:: ObjectFieldNode
+.. autoclass:: ObjectTypeDefinitionNode
+.. autoclass:: ObjectTypeExtensionNode
+.. autoclass:: ObjectValueNode
+.. autoclass:: OperationDefinitionNode
+.. autoclass:: OperationType
+.. autoclass:: OperationTypeDefinitionNode
+.. autoclass:: ScalarTypeDefinitionNode
+.. autoclass:: ScalarTypeExtensionNode
+.. autoclass:: SchemaDefinitionNode
+.. autoclass:: SchemaExtensionNode
+.. autoclass:: SelectionNode
+.. autoclass:: SelectionSetNode
+.. autoclass:: StringValueNode
+.. autoclass:: TypeDefinitionNode
+.. autoclass:: TypeExtensionNode
+.. autoclass:: TypeNode
+.. autoclass:: TypeSystemDefinitionNode
+.. autoclass:: TypeSystemExtensionNode
+.. autoclass:: UnionTypeDefinitionNode
+.. autoclass:: UnionTypeExtensionNode
+.. autoclass:: ValueNode
+.. autoclass:: VariableDefinitionNode
+.. autoclass:: VariableNode
+
+Lexer
+-----
+
+.. autoclass:: Lexer
+.. autoclass:: TokenKind
+.. autoclass:: Token
+
+Location
+--------
+
+.. autofunction:: get_location
+.. autoclass:: SourceLocation
+.. autofunction:: print_location
+
+Parser
+------
+
+.. autofunction:: parse
+.. autofunction:: parse_type
+.. autofunction:: parse_value
+
+Source
+------
+
+.. autoclass:: Source
+.. autofunction:: print_source_location
+
+Visitor
+-------
+
+.. autofunction:: visit
+.. autoclass:: Visitor
+.. autoclass:: ParallelVisitor
+
+The module also exports the following special symbols which can be used as
+return values in the :class:`Visitor` methods to signal particular actions:
+
+.. data:: BREAK
+   :annotation: (same as ``True``)
+
+   This return value signals that no further nodes shall be visited.
+
+.. data:: SKIP
+   :annotation: (same as ``False``)
+
+   This return value signals that the current node shall be skipped.
+
+.. data:: REMOVE
+   :annotation: (same as``Ellipsis``)
+
+   This return value signals that the current node shall be deleted.
+
+.. data:: IDLE
+   :annotation: = None
+
+   This return value signals that no additional action shall take place.
diff --git a/docs/modules/pyutils.rst b/docs/modules/pyutils.rst
new file mode 100644
index 0000000..b11d6ee
--- /dev/null
+++ b/docs/modules/pyutils.rst
@@ -0,0 +1,32 @@
+PyUtils
+=======
+
+.. currentmodule:: graphql.pyutils
+
+.. automodule:: graphql.pyutils
+
+.. autofunction:: camel_to_snake
+.. autofunction:: snake_to_camel
+.. autofunction:: cached_property
+.. autofunction:: did_you_mean
+.. autofunction:: register_description
+.. autofunction:: unregister_description
+.. autoclass:: EventEmitter
+   :members:
+.. autoclass:: EventEmitterAsyncIterator
+   :members:
+.. autofunction:: identity_func
+.. autofunction:: inspect
+.. autofunction:: is_finite
+.. autofunction:: is_integer
+.. autoclass:: AwaitableOrValue
+   :members:
+.. autofunction:: suggestion_list
+.. autoclass:: FrozenError
+.. autoclass:: FrozenList
+.. autoclass:: FrozenDict
+.. autoclass:: Path
+   :members:
+.. autofunction:: print_path_list
+
+.. autodata:: Undefined
diff --git a/docs/modules/subscription.rst b/docs/modules/subscription.rst
new file mode 100644
index 0000000..2dfc4a1
--- /dev/null
+++ b/docs/modules/subscription.rst
@@ -0,0 +1,10 @@
+Subscription
+============
+
+.. currentmodule:: graphql.subscription
+
+.. automodule:: graphql.subscription
+
+.. autofunction:: subscribe
+.. autofunction:: create_source_event_stream
+
diff --git a/docs/modules/type.rst b/docs/modules/type.rst
new file mode 100644
index 0000000..91ed899
--- /dev/null
+++ b/docs/modules/type.rst
@@ -0,0 +1,195 @@
+Type
+====
+
+.. currentmodule:: graphql.type
+
+.. automodule:: graphql.type
+
+
+Definition
+----------
+
+Predicates
+^^^^^^^^^^
+
+.. autofunction:: is_composite_type
+.. autofunction:: is_enum_type
+.. autofunction:: is_input_object_type
+.. autofunction:: is_input_type
+.. autofunction:: is_interface_type
+.. autofunction:: is_leaf_type
+.. autofunction:: is_list_type
+.. autofunction:: is_named_type
+.. autofunction:: is_non_null_type
+.. autofunction:: is_nullable_type
+.. autofunction:: is_object_type
+.. autofunction:: is_output_type
+.. autofunction:: is_scalar_type
+.. autofunction:: is_type
+.. autofunction:: is_union_type
+.. autofunction:: is_wrapping_type
+
+Assertions
+^^^^^^^^^^
+
+.. autofunction:: assert_abstract_type
+.. autofunction:: assert_composite_type
+.. autofunction:: assert_enum_type
+.. autofunction:: assert_input_object_type
+.. autofunction:: assert_input_type
+.. autofunction:: assert_interface_type
+.. autofunction:: assert_leaf_type
+.. autofunction:: assert_list_type
+.. autofunction:: assert_named_type
+.. autofunction:: assert_non_null_type
+.. autofunction:: assert_nullable_type
+.. autofunction:: assert_object_type
+.. autofunction:: assert_output_type
+.. autofunction:: assert_scalar_type
+.. autofunction:: assert_type
+.. autofunction:: assert_union_type
+.. autofunction:: assert_wrapping_type
+
+Un-modifiers
+^^^^^^^^^^^^
+
+.. autofunction:: get_nullable_type
+.. autofunction:: get_named_type
+
+Definitions
+^^^^^^^^^^^
+.. autoclass:: GraphQLEnumType
+.. autoclass:: GraphQLInputObjectType
+.. autoclass:: GraphQLInterfaceType
+.. autoclass:: GraphQLObjectType
+.. autoclass:: GraphQLScalarType
+.. autoclass:: GraphQLUnionType
+
+Type Wrappers
+^^^^^^^^^^^^^
+
+.. autoclass:: GraphQLList
+.. autoclass:: GraphQLNonNull
+
+Types
+^^^^^
+.. autoclass:: GraphQLAbstractType
+.. autoclass:: GraphQLArgument
+.. autoclass:: GraphQLArgumentMap
+.. autoclass:: GraphQLCompositeType
+.. autoclass:: GraphQLEnumValue
+.. autoclass:: GraphQLEnumValueMap
+.. autoclass:: GraphQLField
+.. autoclass:: GraphQLFieldMap
+.. autoclass:: GraphQLInputField
+.. autoclass:: GraphQLInputFieldMap
+.. autoclass:: GraphQLInputType
+.. autoclass:: GraphQLLeafType
+.. autoclass:: GraphQLNamedType
+.. autoclass:: GraphQLNullableType
+.. autoclass:: GraphQLOutputType
+.. autoclass:: GraphQLType
+.. autoclass:: GraphQLWrappingType
+
+.. autoclass:: Thunk
+
+Resolvers
+^^^^^^^^^
+
+.. autoclass:: GraphQLFieldResolver
+.. autoclass:: GraphQLIsTypeOfFn
+.. autoclass:: GraphQLResolveInfo
+.. autoclass:: GraphQLTypeResolver
+
+
+Directives
+----------
+
+Predicates
+^^^^^^^^^^
+
+.. autofunction:: is_directive
+.. autofunction:: is_specified_directive
+
+Definitions
+^^^^^^^^^^^
+
+.. autoclass:: GraphQLDirective
+.. autoclass:: GraphQLIncludeDirective
+.. autoclass:: GraphQLSkipDirective
+.. autoclass:: GraphQLDeprecatedDirective
+
+.. autodata:: specified_directives
+
+.. data:: DEFAULT_DEPRECATION_REASON
+   :annotation: = 'No longer supported'
+
+   String constant that can be used as the default value for ``deprecation_reason``.
+
+
+Introspection
+-------------
+
+Predicates
+^^^^^^^^^^
+
+.. autofunction:: is_introspection_type
+
+Definitions
+^^^^^^^^^^^
+
+.. autoclass:: TypeKind
+.. autoclass:: TypeMetaFieldDef
+.. autoclass:: TypeNameMetaFieldDef
+.. autoclass:: SchemaMetaFieldDef
+
+.. autodata:: introspection_types
+
+
+Scalars
+-------
+
+Predicates
+^^^^^^^^^^
+
+.. autofunction:: is_specified_scalar_type
+
+Definitions
+^^^^^^^^^^^
+
+.. autoclass:: GraphQLBoolean
+.. autoclass:: GraphQLFloat
+.. autoclass:: GraphQLID
+.. autoclass:: GraphQLInt
+.. autoclass:: GraphQLString
+
+The list of all specified directives is available as
+:data:`specified_directives`.
+
+
+Schema
+------
+
+Predicates
+^^^^^^^^^^
+
+.. autofunction:: is_schema
+
+Definitions
+^^^^^^^^^^^
+
+.. autoclass:: GraphQLSchema
+
+
+Validate
+--------
+
+Functions:
+^^^^^^^^^^
+
+.. autofunction:: validate_schema
+
+Assertions
+^^^^^^^^^^
+
+.. autofunction:: assert_valid_schema
diff --git a/docs/modules/utilities.rst b/docs/modules/utilities.rst
new file mode 100644
index 0000000..bb0ae33
--- /dev/null
+++ b/docs/modules/utilities.rst
@@ -0,0 +1,110 @@
+Utilities
+=========
+
+.. currentmodule:: graphql.utilities
+
+.. automodule:: graphql.utilities
+
+The GraphQL query recommended for a full schema introspection:
+
+.. autofunction:: get_introspection_query
+
+Get the target Operation from a Document:
+
+.. autofunction:: get_operation_ast
+
+Get the Type for the target Operation AST:
+
+.. autofunction:: get_operation_root_type
+
+Convert a GraphQLSchema to an IntrospectionQuery:
+
+.. autofunction:: introspection_from_schema
+
+Build a GraphQLSchema from an introspection result:
+
+.. autofunction:: build_client_schema
+
+Build a GraphQLSchema from GraphQL Schema language:
+
+.. autofunction:: build_ast_schema
+.. autofunction:: build_schema
+
+Extend an existing GraphQLSchema from a parsed GraphQL Schema language AST:
+
+.. autofunction:: extend_schema
+
+Sort a GraphQLSchema:
+
+.. autofunction:: lexicographic_sort_schema
+
+Print a GraphQLSchema to GraphQL Schema language:
+
+.. autofunction:: print_introspection_schema
+.. autofunction:: print_schema
+.. autofunction:: print_type
+.. autofunction:: print_value
+
+Create a GraphQLType from a GraphQL language AST:
+
+.. autofunction:: type_from_ast
+
+Create a Python value from a GraphQL language AST with a type:
+
+.. autofunction:: value_from_ast
+
+Create a Python value from a GraphQL language AST without a type:
+
+.. autofunction:: value_from_ast_untyped
+
+Create a GraphQL language AST from a Python value:
+
+.. autofunction:: ast_from_value
+
+A helper to use within recursive-descent visitors which need to be aware of the GraphQL
+type system:
+
+.. autoclass:: TypeInfo
+.. autoclass:: TypeInfoVisitor
+
+Coerce a Python value to a GraphQL type, or produce errors:
+
+.. autofunction:: coerce_input_value
+
+Concatenate multiple ASTs together:
+
+.. autofunction:: concat_ast
+
+Separate an AST into an AST per Operation:
+
+.. autofunction:: separate_operations
+
+Strip characters that are not significant to the validity or execution
+of a GraphQL document:
+
+.. autofunction:: strip_ignored_characters
+
+Comparators for types:
+
+.. autofunction:: is_equal_type
+.. autofunction:: is_type_sub_type_of
+.. autofunction:: do_types_overlap
+
+Assert that a string is a valid GraphQL name:
+
+.. autofunction:: assert_valid_name
+.. autofunction:: is_valid_name_error
+
+Compare two GraphQLSchemas and detect breaking changes:
+
+.. autofunction:: find_breaking_changes
+.. autofunction:: find_dangerous_changes
+
+.. autoclass:: BreakingChange
+.. autoclass:: BreakingChangeType
+.. autoclass:: DangerousChange
+.. autoclass:: DangerousChangeType
+
+Report all deprecated usages within a GraphQL document:
+
+.. autofunction:: find_deprecated_usages
diff --git a/docs/modules/validation.rst b/docs/modules/validation.rst
new file mode 100644
index 0000000..7dbb836
--- /dev/null
+++ b/docs/modules/validation.rst
@@ -0,0 +1,145 @@
+Validation
+==========
+
+.. currentmodule:: graphql.validation
+
+.. automodule:: graphql.validation
+
+.. autofunction:: validate
+
+.. autoclass:: ASTValidationContext
+
+.. autoclass:: ASTValidationRule
+
+.. autoclass:: SDLValidationContext
+
+.. autoclass:: SDLValidationRule
+
+.. autoclass:: ValidationContext
+
+.. autoclass:: ValidationRule
+
+
+Rules
+-----
+
+This list includes all validation rules defined by the GraphQL spec. The order of the
+rules in this list has been adjusted to lead to the most clear output when encountering
+multiple validation errors:
+
+.. autodata:: specified_rules
+   :annotation: = FrozenList([...])
+
+Spec Section: "Executable Definitions"
+
+.. autoclass:: ExecutableDefinitionsRule
+
+Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
+
+.. autoclass:: FieldsOnCorrectTypeRule
+
+Spec Section: "Fragments on Composite Types"
+
+.. autoclass:: FragmentsOnCompositeTypesRule
+
+Spec Section: "Argument Names"
+
+.. autoclass:: KnownArgumentNamesRule
+
+Spec Section: "Directives Are Defined"
+
+.. autoclass:: KnownDirectivesRule
+
+Spec Section: "Fragment spread target defined"
+
+.. autoclass:: KnownFragmentNamesRule
+
+Spec Section: "Fragment Spread Type Existence"
+
+.. autoclass:: KnownTypeNamesRule
+
+Spec Section: "Lone Anonymous Operation"
+
+.. autoclass:: LoneAnonymousOperationRule
+
+Spec Section: "Fragments must not form cycles"
+
+.. autoclass:: NoFragmentCyclesRule
+
+Spec Section: "All Variable Used Defined"
+
+.. autoclass:: NoUndefinedVariablesRule
+
+Spec Section: "Fragments must be used"
+
+.. autoclass:: NoUnusedFragmentsRule
+
+Spec Section: "All Variables Used"
+
+.. autoclass:: NoUnusedVariablesRule
+
+Spec Section: "Field Selection Merging"
+
+.. autoclass:: OverlappingFieldsCanBeMergedRule
+
+Spec Section: "Fragment spread is possible"
+
+.. autoclass:: PossibleFragmentSpreadsRule
+
+Spec Section: "Argument Optionality"
+
+.. autoclass:: ProvidedRequiredArgumentsRule
+
+Spec Section: "Leaf Field Selections"
+
+.. autoclass:: ScalarLeafsRule
+
+Spec Section: "Subscriptions with Single Root Field"
+
+.. autoclass:: SingleFieldSubscriptionsRule
+
+Spec Section: "Argument Uniqueness"
+
+.. autoclass:: UniqueArgumentNamesRule
+
+Spec Section: "Directives Are Unique Per Location"
+
+.. autoclass:: UniqueDirectivesPerLocationRule
+
+Spec Section: "Fragment Name Uniqueness"
+
+.. autoclass:: UniqueFragmentNamesRule
+
+Spec Section: "Input Object Field Uniqueness"
+
+.. autoclass:: UniqueInputFieldNamesRule
+
+Spec Section: "Operation Name Uniqueness"
+
+.. autoclass:: UniqueOperationNamesRule
+
+Spec Section: "Variable Uniqueness"
+
+.. autoclass:: UniqueVariableNamesRule
+
+Spec Section: "Value Type Correctness"
+
+.. autoclass:: ValuesOfCorrectTypeRule
+
+Spec Section: "Variables are Input Types"
+
+.. autoclass:: VariablesAreInputTypesRule
+
+Spec Section: "All Variable Usages Are Allowed"
+
+.. autoclass:: VariablesInAllowedPositionRule
+
+SDL-specific validation rules
+
+.. autoclass:: LoneSchemaDefinitionRule
+.. autoclass:: UniqueOperationTypesRule
+.. autoclass:: UniqueTypeNamesRule
+.. autoclass:: UniqueEnumValueNamesRule
+.. autoclass:: UniqueFieldDefinitionNamesRule
+.. autoclass:: UniqueDirectiveNamesRule
+.. autoclass:: PossibleTypeExtensionsRule
diff --git a/docs/requirements.txt b/docs/requirements.txt
new file mode 100644
index 0000000..c559370
--- /dev/null
+++ b/docs/requirements.txt
@@ -0,0 +1,2 @@
+sphinx>=2.2,<3
+sphinx_rtd_theme>=0.4
\ No newline at end of file
diff --git a/docs/usage/extension.rst b/docs/usage/extension.rst
new file mode 100644
index 0000000..4d6c49b
--- /dev/null
+++ b/docs/usage/extension.rst
@@ -0,0 +1,49 @@
+Extending a Schema
+------------------
+
+With GraphQL-core 3 you can also extend a given schema using type extensions. For
+example, we might want to add a ``lastName`` property to our ``Human`` data type to
+retrieve only the last name of the person.
+
+This can be achieved with the :func:`graphql.utilities.extend_schema` function as
+follows::
+
+    from graphql import extend_schema, parse
+
+    schema = extend_schema(schema, parse("""
+        extend type Human {
+         lastName: String
+        }
+        """))
+
+Note that this function expects the extensions as an AST, which we can get using the
+:func:`graphql.language.parse` function. Also note that the :func:`~graphql.extend_schema` function
+does not alter the original schema, but returns a new schema object.
+
+We also need to attach a resolver function to the new field::
+
+    def get_last_name(human, info):
+        return human['name'].rsplit(None, 1)[-1]
+
+    schema.get_type('Human').fields['lastName'].resolve = get_last_name
+
+Now we can query only the last name of a human::
+
+    from graphql import graphql_sync
+
+    result = graphql_sync(schema, """
+        {
+          human(id: "1000") {
+            lastName
+            homePlanet
+          }
+        }
+        """)
+    print(result)
+
+This query will give the following result::
+
+    ExecutionResult(
+        data={'human': {'lastName': 'Skywalker', 'homePlanet': 'Tatooine'}},
+        errors=None)
+
diff --git a/docs/usage/index.rst b/docs/usage/index.rst
new file mode 100644
index 0000000..11af8af
--- /dev/null
+++ b/docs/usage/index.rst
@@ -0,0 +1,19 @@
+Usage
+=====
+
+GraphQL-core provides two important capabilities: building a type schema, and serving
+queries against that type schema.
+
+.. toctree::
+   :maxdepth: 2
+
+   schema
+   resolvers
+   queries
+   sdl
+   methods
+   introspection
+   parser
+   extension
+   validator
+   other
diff --git a/docs/usage/introspection.rst b/docs/usage/introspection.rst
new file mode 100644
index 0000000..095ce5e
--- /dev/null
+++ b/docs/usage/introspection.rst
@@ -0,0 +1,63 @@
+Using an Introspection Query
+----------------------------
+
+A third way of building a schema is using an introspection query on an existing server.
+This is what GraphiQL uses to get information about the schema on the remote server. You
+can create an introspection query using GraphQL-core 3 with::
+
+    from graphql import get_introspection_query
+
+    query = get_introspection_query(descriptions=True)
+
+This will also yield the descriptions of the introspected schema fields. You can also
+create a query that omits the descriptions with::
+
+    query = get_introspection_query(descriptions=False)
+
+In practice you would run this query against a remote server, but we can also run it
+against the schema we have just built above::
+
+    from graphql import graphql_sync
+
+    introspection_query_result = graphql_sync(schema, query)
+
+The ``data`` attribute of the introspection query result now gives us a dictionary,
+which constitutes a third way of describing a GraphQL schema::
+
+    {'__schema': {
+        'queryType': {'name': 'Query'},
+        'mutationType': None, 'subscriptionType': None,
+        'types': [
+            {'kind': 'OBJECT', 'name': 'Query', 'description': None,
+             'fields': [{
+                'name': 'hero', 'description': None,
+                'args': [{'name': 'episode', 'description': ... }],
+                ... }, ... ], ... },
+            ... ],
+        ... }
+    }
+
+This result contains all the information that is available in the SDL description of the
+schema, i.e. it does not contain the resolve functions and information on the
+server-side values of the enum types.
+
+You can convert the introspection result into ``GraphQLSchema`` with GraphQL-core 3 by
+using the :func:`graphql.utilities.build_client_schema` function::
+
+    from graphql import build_client_schema
+
+    client_schema = build_client_schema(introspection_query_result.data)
+
+
+It is also possible to convert the result to SDL with GraphQL-core 3 by using the
+:func:`graphql.utilities.print_schema` function::
+
+    from graphql import print_schema
+
+    sdl = print_schema(client_schema)
+    print(sdl)
+
+This prints the SDL representation of the schema that we started with.
+
+As you see, it is easy to convert between the three forms of representing a GraphQL
+schema in GraphQL-core 3.
diff --git a/docs/usage/methods.rst b/docs/usage/methods.rst
new file mode 100644
index 0000000..64f8e30
--- /dev/null
+++ b/docs/usage/methods.rst
@@ -0,0 +1,57 @@
+Using resolver methods
+----------------------
+
+Above we have attached resolver functions to the schema only. However, it is also
+possible to define resolver methods on the resolved objects, starting with the
+``root_value`` object that you can pass to the :func:`graphql.graphql` function when
+executing a query.
+
+In our case, we could create a ``Root`` class with three methods as root resolvers, like
+so::
+
+    class Root:
+        """The root resolvers"""
+
+        def hero(self, info, episode):
+            return luke if episode == 5 else artoo
+
+        def human(self, info, id):
+            return human_data.get(id)
+
+        def droid(self, info, id):
+            return droid_data.get(id)
+
+
+Since we have defined synchronous methods only, we will use the
+:func:`graphql.graphql_sync` function to execute a query, passing a ``Root()`` object as
+the ``root_value``::
+
+    from graphql import graphql_sync
+
+    result = graphql_sync(schema, """
+        {
+          droid(id: "2001") {
+            name
+            primaryFunction
+          }
+        }
+        """, Root())
+    print(result)
+
+Even if we haven't attached a resolver to the ``hero`` field as we did above, this would
+now still resolve and give the following output::
+
+    ExecutionResult(
+        data={'droid': {'name': 'R2-D2', 'primaryFunction': 'Astromech'}},
+        errors=None)
+
+Of course you can also define asynchronous methods as resolvers, and execute queries
+asynchronously with :func:`graphql.graphql`.
+
+In a similar vein, you can also attach resolvers as methods to the resolved objects on
+deeper levels than the root of the query. In that case, instead of resolving to
+dictionaries with keys for all the fields, as we did above, you would resolve to objects
+with attributes for all the fields. For instance, you would define a class ``Human``
+with a method ``friends()`` for resolving the friends of a human. You can also make
+use of inheritance in this case. The ``Human`` class and a ``Droid`` class could inherit
+from a ``Character`` class and use its methods as resolvers for common fields.
diff --git a/docs/usage/other.rst b/docs/usage/other.rst
new file mode 100644
index 0000000..fc58b3c
--- /dev/null
+++ b/docs/usage/other.rst
@@ -0,0 +1,25 @@
+Subscriptions
+-------------
+
+.. currentmodule:: graphql.subscription
+
+Sometimes you need to not only query data from a server, but you also want to push data
+from the server to the client. GraphQL-core 3 has you also covered here, because it
+implements the "Subscribe" algorithm described in the GraphQL spec. To execute a GraphQL
+subscription, you must use the :func:`subscribe` method from the
+:mod:`graphql.subscription` module. Instead of a single ``ExecutionResult``, this
+function returns an asynchronous iterator yielding a stream of those, unless there was
+an immediate error. Of course you will then also need to maintain a persistent channel
+to the client (often realized via WebSockets) to push these results back.
+
+
+Other Usages
+------------
+
+GraphQL-core 3 provides many more low-level functions that can be used to work with
+GraphQL schemas and queries. We encourage you to explore the contents of the various
+:ref:`sub-packages`, particularly :mod:`graphql.utilities`, and to look into the source
+code and tests of `GraphQL-core 3`_ in order to find all the functionality that is
+provided and understand it in detail.
+
+.. _GraphQL-core 3: https://github.com/graphql-python/graphql-core
diff --git a/docs/usage/parser.rst b/docs/usage/parser.rst
new file mode 100644
index 0000000..8335d7c
--- /dev/null
+++ b/docs/usage/parser.rst
@@ -0,0 +1,74 @@
+Parsing GraphQL Queries and Schema Notation
+-------------------------------------------
+
+.. currentmodule:: graphql.language
+
+When executing GraphQL queries, the first step that happens under the hood is parsing
+the query. But GraphQL-core 3 also exposes the parser for direct usage via the
+:func:`graphql.language.parse` function. When you pass this function a GraphQL source
+code, it will be parsed and returned as a Document, i.e. an abstract syntax tree (AST)
+of :class:`graphql.language.Node` objects. The root node will be a
+:class:`graphql.language.DocumentNode`, with child nodes of different kinds
+corresponding to the GraphQL source. The nodes also carry information on the location in
+the source code that they correspond to.
+
+Here is an example::
+
+    from graphql import parse
+
+    document = parse("""
+        type Query {
+          me: User
+        }
+
+        type User {
+          id: ID
+          name: String
+        }
+    """)
+
+You can also leave out the information on the location in the source code when creating
+the AST document::
+
+    document = parse(..., no_location=True)
+
+This will give the same result as manually creating the AST document::
+
+    from graphql.language.ast import *
+
+    document = DocumentNode(definitions=[
+        ObjectTypeDefinitionNode(
+            name=NameNode(value='Query'),
+            fields=[
+                FieldDefinitionNode(
+                    name=NameNode(value='me'),
+                    type=NamedTypeNode(name=NameNode(value='User')),
+                    arguments=[], directives=[])
+                ], directives=[], interfaces=[]),
+        ObjectTypeDefinitionNode(
+            name=NameNode(value='User'),
+            fields=[
+                FieldDefinitionNode(
+                    name=NameNode(value='id'),
+                    type=NamedTypeNode(
+                        name=NameNode(value='ID')),
+                    arguments=[], directives=[]),
+                FieldDefinitionNode(
+                    name=NameNode(value='name'),
+                    type=NamedTypeNode(
+                        name=NameNode(value='String')),
+                    arguments=[], directives=[]),
+                ], directives=[], interfaces=[]),
+        ])
+
+
+When parsing with ``no_location=False`` (the default), the AST nodes will also have a
+``loc`` attribute carrying the information on the source code location corresponding
+to the AST nodes.
+
+When there is a syntax error in the GraphQL source code, then the :func:`parse` function
+will raise a :exc:`graphql.error.GraphQLSyntaxError`.
+
+The parser can not only be used to parse GraphQL queries, but also to parse the GraphQL
+schema definition language. This will result in another way of representing a GraphQL
+schema, as an AST document.
diff --git a/docs/usage/queries.rst b/docs/usage/queries.rst
new file mode 100644
index 0000000..623fd25
--- /dev/null
+++ b/docs/usage/queries.rst
@@ -0,0 +1,129 @@
+Executing Queries
+-----------------
+
+.. currentmodule:: graphql.execution
+
+Now that we have defined the schema and breathed life into it with our resolver
+functions, we can execute arbitrary query against the schema.
+
+The :mod:`graphql` package provides the :func:`graphql.graphql` function to execute
+queries. This is the main feature of GraphQL-core.
+
+Note however that this function is actually a coroutine intended to be used in
+asynchronous code running in an event loop.
+
+Here is one way to use it::
+
+    import asyncio
+    from graphql import graphql
+
+    async def query_artoo():
+        result = await graphql(schema, """
+        {
+          droid(id: "2001") {
+            name
+            primaryFunction
+          }
+        }
+        """)
+        print(result)
+
+    asyncio.get_event_loop().run_until_complete(query_artoo())
+
+In our query, we asked for the droid with the id 2001, which is R2-D2, and its primary
+function, Astromech. When everything has been implemented correctly as shown above, you
+should get the expected result::
+
+    ExecutionResult(
+        data={'droid': {'name': 'R2-D2', 'primaryFunction': 'Astromech'}},
+        errors=None)
+
+The :class:`ExecutionResult` has a ``data`` attribute with the actual result, and an
+``errors`` attribute with a list of errors if there were any.
+
+If all your resolvers work synchronously, as in our case, you can also use the
+:func:`graphql.graphql_sync` function to query the result in ordinary synchronous code::
+
+    from graphql import graphql_sync
+
+    result = graphql_sync(schema, """
+      query FetchHuman($id: String!) {
+        human(id: $id) {
+          name
+          homePlanet
+        }
+      }
+      """, variable_values={'id': '1000'})
+    print(result)
+
+Here we asked for the human with the id 1000, Luke Skywalker, and his home planet,
+Tatooine. So the output of the code above is::
+
+    ExecutionResult(
+        data={'human': {'name': 'Luke Skywalker', 'homePlanet': 'Tatooine'}},
+        errors=None)
+
+Let's see what happens when we make a mistake in the query, by querying a non-existing
+``homeTown`` field::
+
+    result = graphql_sync(schema, """
+        {
+          human(id: "1000") {
+            name
+            homePlace
+          }
+        }
+        """)
+    print(result)
+
+You will get the following result as output::
+
+    ExecutionResult(data=None, errors=[GraphQLError(
+        "Cannot query field 'homePlace' on type 'Human'. Did you mean 'homePlanet'?",
+        locations=[SourceLocation(line=5, column=9)])])
+
+This is very helpful. Not only do we get the exact location of the mistake in the query,
+but also a suggestion for correcting the bad field name.
+
+GraphQL also allows to request the meta field ``__typename``. We can use this to verify
+that the hero of "The Empire Strikes Back" episode is Luke Skywalker and that he is in
+fact a human::
+
+    result = graphql_sync(schema, """
+        {
+          hero(episode: EMPIRE) {
+            __typename
+            name
+          }
+        }
+        """)
+    print(result)
+
+This gives the following output::
+
+    ExecutionResult(
+        data={'hero': {'__typename': 'Human', 'name': 'Luke Skywalker'}},
+        errors=None)
+
+Finally, let's see what happens when we try to access the secret backstory of our hero::
+
+    result = graphql_sync(schema, """
+        {
+          hero(episode: EMPIRE) {
+            name
+            secretBackstory
+          }
+        }
+        """)
+    print(result)
+
+While we get the name of the hero, the secret backstory fields remains empty, since its
+resolver function raises an error. However, we get the error that has been raised by the
+resolver in the ``errors`` attribute of the result::
+
+    ExecutionResult(
+        data={'hero': {'name': 'Luke Skywalker', 'secretBackstory': None}},
+        errors=[GraphQLError('secretBackstory is secret.',
+                locations=[SourceLocation(line=5, column=9)],
+                path=['hero', 'secretBackstory'])])
+
diff --git a/docs/usage/resolvers.rst b/docs/usage/resolvers.rst
new file mode 100644
index 0000000..d75d75b
--- /dev/null
+++ b/docs/usage/resolvers.rst
@@ -0,0 +1,97 @@
+Implementing the Resolver Functions
+-----------------------------------
+
+Before we can execute queries against our schema, we also need to define the data (the
+humans and droids appearing in the Star Wars trilogy) and implement resolver functions
+that fetch the data (at the beginning of our schema module, because we are referencing
+them later)::
+
+    luke = dict(
+        id='1000', name='Luke Skywalker', homePlanet='Tatooine',
+        friends=['1002', '1003', '2000', '2001'], appearsIn=[4, 5, 6])
+
+    vader = dict(
+        id='1001', name='Darth Vader', homePlanet='Tatooine',
+        friends=['1004'], appearsIn=[4, 5, 6])
+
+    han = dict(
+        id='1002', name='Han Solo', homePlanet=None,
+        friends=['1000', '1003', '2001'], appearsIn=[4, 5, 6])
+
+    leia = dict(
+        id='1003', name='Leia Organa', homePlanet='Alderaan',
+        friends=['1000', '1002', '2000', '2001'], appearsIn=[4, 5, 6])
+
+    tarkin = dict(
+        id='1004', name='Wilhuff Tarkin', homePlanet=None,
+        friends=['1001'], appearsIn=[4])
+
+    human_data = {
+        '1000': luke, '1001': vader, '1002': han, '1003': leia, '1004': tarkin}
+
+    threepio = dict(
+        id='2000', name='C-3PO', primaryFunction='Protocol',
+        friends=['1000', '1002', '1003', '2001'], appearsIn=[4, 5, 6])
+
+    artoo = dict(
+        id='2001', name='R2-D2', primaryFunction='Astromech',
+        friends=['1000', '1002', '1003'], appearsIn=[4, 5, 6])
+
+    droid_data = {
+        '2000': threepio, '2001': artoo}
+
+
+    def get_character_type(character, _info, _type):
+            return 'Droid' if character['id'] in droid_data else 'Human'
+
+
+    def get_character(id):
+        """Helper function to get a character by ID."""
+        return human_data.get(id) or droid_data.get(id)
+
+
+    def get_friends(character, _info):
+        """Allows us to query for a character's friends."""
+        return map(get_character, character.friends)
+
+
+    def get_hero(root, _info, episode):
+        """Allows us to fetch the undisputed hero of the trilogy, R2-D2."""
+        if episode == 5:
+            return luke  # Luke is the hero of Episode V
+        return artoo  # Artoo is the hero otherwise
+
+
+    def get_human(root, _info, id):
+        """Allows us to query for the human with the given id."""
+        return human_data.get(id)
+
+
+    def get_droid(root, _info, id):
+        """Allows us to query for the droid with the given id."""
+        return droid_data.get(id)
+
+
+    def get_secret_backstory(_character, _info):
+        """Raise an error when attempting to get the secret backstory."""
+        raise RuntimeError('secretBackstory is secret.')
+
+
+Note that the resolver functions get the current object as first argument. For a field
+on the root Query type this is often not used, but a root object can also be defined
+when executing the query. As the second argument, they get an object containing
+execution information, as defined in the :class:`graphql.type.GraphQLResolveInfo` class.
+This object also has a ``context`` attribute that can be used to provide every resolver
+with contextual information like the currently logged in user, or a database session.
+In our simple example we don't authenticate users and use static data instead of a
+database, so we don't make use of it here. In addition to these two arguments,
+resolver functions optionally get the defined for the field in the schema, using the
+same names (the names are not translated from GraphQL naming conventions to Python
+naming conventions).
+
+Also note that you don't need to provide resolvers for simple attribute access or for
+fetching items from Python dictionaries.
+
+Finally, note that our data uses the internal values of the ``Episode`` enum that we
+have defined above, not the descriptive enum names that are used externally. For
+example, ``NEWHOPE`` ("A New Hope") has internally the actual episode number 4 as value.
diff --git a/docs/usage/schema.rst b/docs/usage/schema.rst
new file mode 100644
index 0000000..4a7dbf6
--- /dev/null
+++ b/docs/usage/schema.rst
@@ -0,0 +1,195 @@
+Building a Type Schema
+----------------------
+
+.. currentmodule:: graphql.type
+
+Using the classes in the :mod:`graphql.type` sub-package as building blocks, you can
+build a complete GraphQL type schema.
+
+Let's take the following schema as an example, which will allow us to query our favorite
+heroes from the Star Wars trilogy::
+
+    enum Episode { NEWHOPE, EMPIRE, JEDI }
+
+    interface Character {
+      id: String!
+      name: String
+      friends: [Character]
+      appearsIn: [Episode]
+    }
+
+    type Human implements Character {
+      id: String!
+      name: String
+      friends: [Character]
+      appearsIn: [Episode]
+      homePlanet: String
+    }
+
+    type Droid implements Character {
+      id: String!
+      name: String
+      friends: [Character]
+      appearsIn: [Episode]
+      primaryFunction: String
+    }
+
+    type Query {
+      hero(episode: Episode): Character
+      human(id: String!): Human
+      droid(id: String!): Droid
+    }
+
+We have been using the so called GraphQL schema definition language (SDL) here to
+describe the schema. While it is also possible to build a schema directly from this
+notation using GraphQL-core 3, let's first create that schema manually by assembling
+the types defined here using Python classes, adding resolver functions written in Python
+for querying the data.
+
+First, we need to import all the building blocks from the :mod:`graphql.type`
+sub-package. Note that you don't need to import from the sub-packages, since nearly
+everything is also available directly in the top :mod:`graphql` package::
+
+    from graphql import (
+        GraphQLArgument, GraphQLEnumType, GraphQLEnumValue,
+        GraphQLField, GraphQLInterfaceType, GraphQLList, GraphQLNonNull,
+        GraphQLObjectType, GraphQLSchema, GraphQLString)
+
+Next, we need to build the enum type ``Episode``::
+
+    episode_enum = GraphQLEnumType('Episode', {
+        'NEWHOPE': GraphQLEnumValue(4, description='Released in 1977.'),
+        'EMPIRE': GraphQLEnumValue(5, description='Released in 1980.'),
+        'JEDI': GraphQLEnumValue(6, description='Released in 1983.')
+        }, description='One of the films in the Star Wars Trilogy')
+
+If you don't need the descriptions for the enum values, you can also define the enum
+type like this using an underlying Python ``Enum`` type::
+
+    from enum import Enum
+
+    class EpisodeEnum(Enum):
+        NEWHOPE = 4
+        EMPIRE = 5
+        JEDI = 6
+
+    episode_enum = GraphQLEnumType(
+        'Episode', EpisodeEnum,
+        description='One of the films in the Star Wars Trilogy')
+
+You can also use a Python dictionary instead of a Python ``Enum`` type to define the
+GraphQL enum type::
+
+    episode_enum = GraphQLEnumType(
+        'Episode', {'NEWHOPE': 4, 'EMPIRE': 5, 'JEDI': 6},
+        description='One of the films in the Star Wars Trilogy')
+
+Our schema also contains a ``Character`` interface. Here is how we build it::
+
+    character_interface = GraphQLInterfaceType('Character', lambda: {
+        'id': GraphQLField(
+            GraphQLNonNull(GraphQLString),
+            description='The id of the character.'),
+        'name': GraphQLField(
+            GraphQLString,
+            description='The name of the character.'),
+        'friends': GraphQLField(
+            GraphQLList(character_interface),
+            description='The friends of the character,'
+                        ' or an empty list if they have none.'),
+        'appearsIn': GraphQLField(
+            GraphQLList(episode_enum),
+            description='Which movies they appear in.'),
+        'secretBackstory': GraphQLField(
+            GraphQLString,
+            description='All secrets about their past.')},
+        resolve_type=get_character_type,
+        description='A character in the Star Wars Trilogy')
+
+Note that we did not pass the dictionary of fields to the ``GraphQLInterfaceType``
+directly, but using a lambda function (a so-called "thunk"). This is necessary because
+the fields are referring back to the character interface that we are just defining.
+Whenever you have such recursive definitions in GraphQL-core, you need to use thunks.
+Otherwise, you can pass everything directly.
+
+Characters in the Star Wars trilogy are either humans or droids. So we define a
+``Human`` and a ``Droid`` type, which both implement the ``Character`` interface::
+
+    human_type = GraphQLObjectType('Human', lambda: {
+        'id': GraphQLField(
+            GraphQLNonNull(GraphQLString),
+            description='The id of the human.'),
+        'name': GraphQLField(
+            GraphQLString,
+            description='The name of the human.'),
+        'friends': GraphQLField(
+            GraphQLList(character_interface),
+            description='The friends of the human,'
+                        ' or an empty list if they have none.',
+            resolve=get_friends),
+        'appearsIn': GraphQLField(
+            GraphQLList(episode_enum),
+            description='Which movies they appear in.'),
+        'homePlanet': GraphQLField(
+            GraphQLString,
+            description='The home planet of the human, or null if unknown.'),
+        'secretBackstory': GraphQLField(
+            GraphQLString,
+            resolve=get_secret_backstory,
+            description='Where are they from'
+                        ' and how they came to be who they are.')},
+        interfaces=[character_interface],
+        description='A humanoid creature in the Star Wars universe.')
+
+    droid_type = GraphQLObjectType('Droid', lambda: {
+        'id': GraphQLField(
+            GraphQLNonNull(GraphQLString),
+            description='The id of the droid.'),
+        'name': GraphQLField(
+            GraphQLString,
+            description='The name of the droid.'),
+        'friends': GraphQLField(
+            GraphQLList(character_interface),
+            description='The friends of the droid,'
+                        ' or an empty list if they have none.',
+            resolve=get_friends,
+        ),
+        'appearsIn': GraphQLField(
+            GraphQLList(episode_enum),
+            description='Which movies they appear in.'),
+        'secretBackstory': GraphQLField(
+            GraphQLString,
+            resolve=get_secret_backstory,
+            description='Construction date and the name of the designer.'),
+        'primaryFunction': GraphQLField(
+            GraphQLString,
+            description='The primary function of the droid.')
+        },
+        interfaces=[character_interface],
+        description='A mechanical creature in the Star Wars universe.')
+
+Now that we have defined all used result types, we can construct the ``Query`` type for
+our schema::
+
+    query_type = GraphQLObjectType('Query', lambda: {
+        'hero': GraphQLField(character_interface, args={
+            'episode': GraphQLArgument(episode_enum, description=(
+                'If omitted, returns the hero of the whole saga.'
+                ' If provided, returns the hero of that particular episode.'))},
+            resolve=get_hero),
+        'human': GraphQLField(human_type, args={
+            'id': GraphQLArgument(
+                GraphQLNonNull(GraphQLString), description='id of the human')},
+            resolve=get_human),
+        'droid': GraphQLField(droid_type, args={
+            'id': GraphQLArgument(
+                GraphQLNonNull(GraphQLString), description='id of the droid')},
+            resolve=get_droid)})
+
+
+Using our query type we can define our schema::
+
+    schema = GraphQLSchema(query_type)
+
+Note that you can also pass a mutation type and a subscription type as additional
+arguments to the ``GraphQLSchema``.
diff --git a/docs/usage/sdl.rst b/docs/usage/sdl.rst
new file mode 100644
index 0000000..1227c52
--- /dev/null
+++ b/docs/usage/sdl.rst
@@ -0,0 +1,85 @@
+Using the Schema Definition Language
+------------------------------------
+
+Above we defined the GraphQL schema as Python code, using the ``GraphQLSchema`` class
+and other classes representing the various GraphQL types.
+
+GraphQL-core 3 also provides a language-agnostic way of defining a GraphQL schema
+using the GraphQL schema definition language (SDL) which is also part of the GraphQL
+specification. To do this, we simply feed the SDL as a string to the
+:func:`graphql.utilities.build_schema` function::
+
+    from graphql import build_schema
+
+    schema = build_schema("""
+
+        enum Episode { NEWHOPE, EMPIRE, JEDI }
+
+        interface Character {
+          id: String!
+          name: String
+          friends: [Character]
+          appearsIn: [Episode]
+        }
+
+        type Human implements Character {
+          id: String!
+          name: String
+          friends: [Character]
+          appearsIn: [Episode]
+          homePlanet: String
+        }
+
+        type Droid implements Character {
+          id: String!
+          name: String
+          friends: [Character]
+          appearsIn: [Episode]
+          primaryFunction: String
+        }
+
+        type Query {
+          hero(episode: Episode): Character
+          human(id: String!): Human
+          droid(id: String!): Droid
+        }
+        """)
+
+The result is a ``GraphQLSchema`` object just like the one we defined above, except for
+the resolver functions which cannot be defined in the SDL.
+
+We would need to manually attach these functions to the schema, like so::
+
+    schema.query_type.fields['hero'].resolve = get_hero
+    schema.get_type('Character').resolve_type = get_character_type
+
+Another problem is that the SDL does not define the server side values of the
+``Episode`` enum type which are returned by the resolver functions and which are
+different from the names used for the episode.
+
+So we would also need to manually define these values, like so::
+
+    for name, value in schema.get_type('Episode').values.items():
+        value.value = EpisodeEnum[name].value
+
+This would allow us to query the schema built from SDL just like the manually assembled
+schema::
+
+    from graphql import graphql_sync
+
+    result = graphql_sync(schema, """
+        {
+          hero(episode: EMPIRE) {
+            name
+            appearsIn
+          }
+        }
+        """)
+    print(result)
+
+And we would get the expected result::
+
+    ExecutionResult(
+        data={'hero': {'name': 'Luke Skywalker',
+                       'appearsIn': ['NEWHOPE', 'EMPIRE', 'JEDI']}},
+        errors=None)
diff --git a/docs/usage/validator.rst b/docs/usage/validator.rst
new file mode 100644
index 0000000..dc696d2
--- /dev/null
+++ b/docs/usage/validator.rst
@@ -0,0 +1,43 @@
+Validating GraphQL Queries
+--------------------------
+
+.. currentmodule:: graphql.validation
+
+When executing GraphQL queries, the second step that happens under the hood after
+parsing the source code is a validation against the given schema using the rules of the
+GraphQL specification. You can also run the validation step manually by calling the
+:func:`graphql.validation.validate` function, passing the schema and the AST document::
+
+    from graphql import parse, validate
+
+    errors = validate(schema, parse("""
+        {
+          human(id: NEWHOPE) {
+            name
+            homePlace
+            friends
+          }
+        }
+        """))
+
+As a result, you will get a complete list of all errors that the validators has found.
+In this case, we will get::
+
+    [GraphQLError(
+        'String cannot represent a non string value: NEWHOPE',
+        locations=[SourceLocation(line=3, column=17)]),
+     GraphQLError(
+        "Cannot query field 'homePlace' on type 'Human'."
+         " Did you mean 'homePlanet'?",
+         locations=[SourceLocation(line=5, column=9)]),
+     GraphQLError(
+        "Field 'friends' of type '[Character]' must have a selection of subfields."
+         "  Did you mean 'friends { ... }'?",
+         locations=[SourceLocation(line=6, column=9)])]
+
+These rules are available in the :data:`specified_rules` list and implemented in the
+``graphql.validation.rules`` subpackage. Instead of the default rules,
+you can also use a subset or create custom rules. The rules are
+based on the :class:`graphql.validation.ValidationRule` class which is based on the
+:class:`graphql.language.Visitor` class which provides a way of walking through an AST
+document using the visitor pattern.
diff --git a/graphql/__init__.py b/graphql/__init__.py
deleted file mode 100644
index 902800b..0000000
--- a/graphql/__init__.py
+++ /dev/null
@@ -1,267 +0,0 @@
-"""
-GraphQL.js provides a reference implementation for the GraphQL specification
-but is also a useful utility for operating on GraphQL files and building
-sophisticated tools.
-
-This primary module exports a general purpose function for fulfilling all
-steps of the GraphQL specification in a single operation, but also includes
-utilities for every part of the GraphQL specification:
-
-  - Parsing the GraphQL language.
-  - Building a GraphQL type schema.
-  - Validating a GraphQL request against a type schema.
-  - Executing a GraphQL request against a type schema.
-
-This also includes utility functions for operating on GraphQL types and
-GraphQL documents to facilitate building tools.
-
-You may also import from each sub-directory directly. For example, the
-following two import statements are equivalent:
-
-    from graphql import parse
-    from graphql.language.base import parse
-"""
-from .pyutils.version import get_version
-
-# The primary entry point into fulfilling a GraphQL request.
-from .graphql import graphql
-
-# Create and operate on GraphQL type definitions and schema.
-from .type import (  # no import order
-    GraphQLSchema,
-    # Definitions
-    GraphQLScalarType,
-    GraphQLObjectType,
-    GraphQLInterfaceType,
-    GraphQLUnionType,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLInputObjectType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLField,
-    GraphQLInputObjectField,
-    GraphQLArgument,
-    # "Enum" of Type Kinds
-    TypeKind,
-    # "Enum" of Directive locations
-    DirectiveLocation,
-    # Scalars
-    GraphQLInt,
-    GraphQLFloat,
-    GraphQLString,
-    GraphQLBoolean,
-    GraphQLID,
-    # Directive definition
-    GraphQLDirective,
-    # Built-in directives defined by the Spec
-    specified_directives,
-    GraphQLSkipDirective,
-    GraphQLIncludeDirective,
-    GraphQLDeprecatedDirective,
-    # Constant Deprecation Reason
-    DEFAULT_DEPRECATION_REASON,
-    # GraphQL Types for introspection.
-    __Schema,
-    __Directive,
-    __DirectiveLocation,
-    __Type,
-    __Field,
-    __InputValue,
-    __EnumValue,
-    __TypeKind,
-    # Meta-field definitions.
-    SchemaMetaFieldDef,
-    TypeMetaFieldDef,
-    TypeNameMetaFieldDef,
-    # Predicates
-    is_type,
-    is_input_type,
-    is_output_type,
-    is_leaf_type,
-    is_composite_type,
-    is_abstract_type,
-    # Un-modifiers
-    get_nullable_type,
-    get_named_type,
-)
-
-# Parse and operate on GraphQL language source files.
-from .language.base import (  # no import order
-    Source,
-    get_location,
-    # Parse
-    parse,
-    parse_value,
-    # Print
-    print_ast,
-    # Visit
-    visit,
-    ParallelVisitor,
-    TypeInfoVisitor,
-    BREAK,
-)
-
-# Execute GraphQL queries.
-from .execution import (  # no import order
-    execute,
-    subscribe,
-    ResolveInfo,
-    MiddlewareManager,
-    middlewares,
-)
-
-# Validate GraphQL queries.
-from .validation import validate, specified_rules  # no import order
-
-# Create and format GraphQL errors.
-from .error import GraphQLError, format_error
-
-# Utilities for operating on GraphQL type schema and parsed sources.
-from .utils.base import (
-    # The GraphQL query recommended for a full schema introspection.
-    introspection_query,
-    # Gets the target Operation from a Document
-    get_operation_ast,
-    # Build a GraphQLSchema from an introspection result.
-    build_client_schema,
-    # Build a GraphQLSchema from a parsed GraphQL Schema language AST.
-    build_ast_schema,
-    # Extends an existing GraphQLSchema from a parsed GraphQL Schema
-    # language AST.
-    extend_schema,
-    # Print a GraphQLSchema to GraphQL Schema language.
-    print_schema,
-    # Create a GraphQLType from a GraphQL language AST.
-    type_from_ast,
-    # Create a JavaScript value from a GraphQL language AST.
-    value_from_ast,
-    # Create a GraphQL language AST from a JavaScript value.
-    ast_from_value,
-    # A helper to use within recursive-descent visitors which need to be aware of
-    # the GraphQL type system.
-    TypeInfo,
-    # Determine if JavaScript values adhere to a GraphQL type.
-    is_valid_value,
-    # Determine if AST values adhere to a GraphQL type.
-    is_valid_literal_value,
-    # Concatenates multiple AST together.
-    concat_ast,
-    # Comparators for types
-    is_equal_type,
-    is_type_sub_type_of,
-    do_types_overlap,
-    # Asserts a string is a valid GraphQL name.
-    assert_valid_name,
-    # Undefined const
-    Undefined,
-)
-
-# Utilities for dynamic execution engines
-from .backend import (
-    GraphQLBackend,
-    GraphQLDocument,
-    GraphQLCoreBackend,
-    GraphQLDeciderBackend,
-    GraphQLCachedBackend,
-    get_default_backend,
-    set_default_backend,
-)
-
-VERSION = (2, 2, 1, "final", 0)
-__version__ = get_version(VERSION)
-
-
-__all__ = (
-    "__version__",
-    "graphql",
-    "GraphQLBoolean",
-    "GraphQLEnumType",
-    "GraphQLEnumValue",
-    "GraphQLFloat",
-    "GraphQLID",
-    "GraphQLInputObjectType",
-    "GraphQLInt",
-    "GraphQLInterfaceType",
-    "GraphQLList",
-    "GraphQLNonNull",
-    "GraphQLField",
-    "GraphQLInputObjectField",
-    "GraphQLArgument",
-    "GraphQLObjectType",
-    "GraphQLScalarType",
-    "GraphQLSchema",
-    "GraphQLString",
-    "GraphQLUnionType",
-    "GraphQLDirective",
-    "specified_directives",
-    "GraphQLSkipDirective",
-    "GraphQLIncludeDirective",
-    "GraphQLDeprecatedDirective",
-    "DEFAULT_DEPRECATION_REASON",
-    "TypeKind",
-    "DirectiveLocation",
-    "__Schema",
-    "__Directive",
-    "__DirectiveLocation",
-    "__Type",
-    "__Field",
-    "__InputValue",
-    "__EnumValue",
-    "__TypeKind",
-    "SchemaMetaFieldDef",
-    "TypeMetaFieldDef",
-    "TypeNameMetaFieldDef",
-    "get_named_type",
-    "get_nullable_type",
-    "is_abstract_type",
-    "is_composite_type",
-    "is_input_type",
-    "is_leaf_type",
-    "is_output_type",
-    "is_type",
-    "BREAK",
-    "ParallelVisitor",
-    "Source",
-    "TypeInfoVisitor",
-    "get_location",
-    "parse",
-    "parse_value",
-    "print_ast",
-    "visit",
-    "execute",
-    "subscribe",
-    "ResolveInfo",
-    "MiddlewareManager",
-    "middlewares",
-    "specified_rules",
-    "validate",
-    "GraphQLError",
-    "format_error",
-    "TypeInfo",
-    "assert_valid_name",
-    "ast_from_value",
-    "build_ast_schema",
-    "build_client_schema",
-    "concat_ast",
-    "do_types_overlap",
-    "extend_schema",
-    "get_operation_ast",
-    "introspection_query",
-    "is_equal_type",
-    "is_type_sub_type_of",
-    "is_valid_literal_value",
-    "is_valid_value",
-    "print_schema",
-    "type_from_ast",
-    "value_from_ast",
-    "get_version",
-    "Undefined",
-    "GraphQLBackend",
-    "GraphQLDocument",
-    "GraphQLCoreBackend",
-    "GraphQLDeciderBackend",
-    "GraphQLCachedBackend",
-    "get_default_backend",
-    "set_default_backend",
-)
diff --git a/graphql/backend/__init__.py b/graphql/backend/__init__.py
deleted file mode 100644
index faeb003..0000000
--- a/graphql/backend/__init__.py
+++ /dev/null
@@ -1,41 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-This module provides a dynamic way of using different
-engines for a GraphQL schema query resolution.
-"""
-
-from .base import GraphQLBackend, GraphQLDocument
-from .core import GraphQLCoreBackend
-from .decider import GraphQLDeciderBackend
-from .cache import GraphQLCachedBackend
-
-
-_default_backend = None
-
-
-def get_default_backend():
-    # type: () -> GraphQLCoreBackend
-    global _default_backend
-    if _default_backend is None:
-        _default_backend = GraphQLCoreBackend()
-    return _default_backend
-
-
-def set_default_backend(backend):
-    # type: (GraphQLCoreBackend) -> None
-    global _default_backend
-    assert isinstance(
-        backend, GraphQLBackend
-    ), "backend must be an instance of GraphQLBackend."
-    _default_backend = backend
-
-
-__all__ = [
-    "GraphQLBackend",
-    "GraphQLDocument",
-    "GraphQLCoreBackend",
-    "GraphQLDeciderBackend",
-    "GraphQLCachedBackend",
-    "get_default_backend",
-    "set_default_backend",
-]
diff --git a/graphql/backend/base.py b/graphql/backend/base.py
deleted file mode 100644
index 6573d3a..0000000
--- a/graphql/backend/base.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from ..pyutils.cached_property import cached_property
-from ..language import ast
-
-from abc import ABCMeta, abstractmethod
-import six
-
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Dict, Optional, Union, Callable
-    from ..language.ast import Document
-    from ..type.schema import GraphQLSchema
-
-
-class GraphQLBackend(six.with_metaclass(ABCMeta)):
-    @abstractmethod
-    def document_from_string(self, schema, request_string):
-        raise NotImplementedError(
-            "document_from_string method not implemented in {}.".format(self.__class__)
-        )
-
-
-class GraphQLDocument(object):
-    def __init__(self, schema, document_string, document_ast, execute):
-        # type: (GraphQLSchema, str, Document, Callable) -> None
-        self.schema = schema
-        self.document_string = document_string
-        self.document_ast = document_ast
-        self.execute = execute
-
-    @cached_property
-    def operations_map(self):
-        # type: () -> Dict[Union[str, None], str]
-        """
-        returns a Mapping of operation names and it's associated types.
-        E.g. {'myQuery': 'query', 'myMutation': 'mutation'}
-        """
-        document_ast = self.document_ast
-        operations = {}  # type: Dict[Union[str, None], str]
-        for definition in document_ast.definitions:
-            if isinstance(definition, ast.OperationDefinition):
-                if definition.name:
-                    operations[definition.name.value] = definition.operation
-                else:
-                    operations[None] = definition.operation
-
-        return operations
-
-    def get_operation_type(self, operation_name):
-        # type: (Optional[str]) -> Optional[str]
-        """
-        Returns the operation type ('query', 'mutation', 'subscription' or None)
-        for the given operation_name.
-        If no operation_name is provided (and only one operation exists) it will return the
-        operation type for that operation
-        """
-        operations_map = self.operations_map
-        if not operation_name and len(operations_map) == 1:
-            return next(iter(operations_map.values()))
-        return operations_map.get(operation_name)
diff --git a/graphql/backend/cache.py b/graphql/backend/cache.py
deleted file mode 100644
index f35a45c..0000000
--- a/graphql/backend/cache.py
+++ /dev/null
@@ -1,80 +0,0 @@
-from hashlib import sha1
-
-from six import string_types
-from ..type import GraphQLSchema
-
-from .base import GraphQLBackend
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Dict, Optional, Hashable
-    from .base import GraphQLDocument
-
-_cached_schemas = {}  # type: Dict[GraphQLSchema, str]
-
-_cached_queries = {}  # type: Dict[str, str]
-
-
-def get_unique_schema_id(schema):
-    # type: (GraphQLSchema) -> str
-    """Get a unique id given a GraphQLSchema"""
-    assert isinstance(schema, GraphQLSchema), (
-        "Must receive a GraphQLSchema as schema. Received {}"
-    ).format(repr(schema))
-
-    if schema not in _cached_schemas:
-        _cached_schemas[schema] = sha1(str(schema).encode("utf-8")).hexdigest()
-    return _cached_schemas[schema]
-
-
-def get_unique_document_id(query_str):
-    # type: (str) -> str
-    """Get a unique id given a query_string"""
-    assert isinstance(query_str, string_types), (
-        "Must receive a string as query_str. Received {}"
-    ).format(repr(query_str))
-
-    if query_str not in _cached_queries:
-        _cached_queries[query_str] = sha1(str(query_str).encode("utf-8")).hexdigest()
-    return _cached_queries[query_str]
-
-
-class GraphQLCachedBackend(GraphQLBackend):
-    """GraphQLCachedBackend will cache the document response from the backend
-    given a key for that document"""
-
-    def __init__(
-        self,
-        backend,  # type: GraphQLBackend
-        cache_map=None,  # type: Optional[Dict[Hashable, GraphQLDocument]]
-        use_consistent_hash=False,  # type: bool
-    ):
-        # type: (...) -> None
-        assert isinstance(
-            backend, GraphQLBackend
-        ), "Provided backend must be an instance of GraphQLBackend"
-        if cache_map is None:
-            cache_map = {}
-        self.backend = backend
-        self.cache_map = cache_map
-        self.use_consistent_hash = use_consistent_hash
-
-    def get_key_for_schema_and_document_string(self, schema, request_string):
-        # type: (GraphQLSchema, str) -> int
-        """This method returns a unique key given a schema and a request_string"""
-        if self.use_consistent_hash:
-            schema_id = get_unique_schema_id(schema)
-            document_id = get_unique_document_id(request_string)
-            return hash((schema_id, document_id))
-        return hash((schema, request_string))
-
-    def document_from_string(self, schema, request_string):
-        # type: (GraphQLSchema, str) -> Optional[GraphQLDocument]
-        """This method returns a GraphQLQuery (from cache if present)"""
-        key = self.get_key_for_schema_and_document_string(schema, request_string)
-        if key not in self.cache_map:
-            self.cache_map[key] = self.backend.document_from_string(
-                schema, request_string
-            )
-
-        return self.cache_map[key]
diff --git a/graphql/backend/compiled.py b/graphql/backend/compiled.py
deleted file mode 100644
index e3805cd..0000000
--- a/graphql/backend/compiled.py
+++ /dev/null
@@ -1,55 +0,0 @@
-from six import string_types
-from .base import GraphQLDocument
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..type.schema import GraphQLSchema
-    from typing import Any, Optional, Dict, Callable, Union
-
-
-class GraphQLCompiledDocument(GraphQLDocument):
-    @classmethod
-    def from_code(
-        cls,
-        schema,  # type: GraphQLSchema
-        code,  # type: Union[str, Any]
-        uptodate=None,  # type: Optional[bool]
-        extra_namespace=None,  # type: Optional[Dict[str, Any]]
-    ):
-        # type: (...) -> GraphQLCompiledDocument
-        """Creates a GraphQLDocument object from compiled code and the globals.  This
-        is used by the loaders and schema to create a document object.
-        """
-        if isinstance(code, string_types):
-            filename = "<document>"
-            code = compile(code, filename, "exec")
-        namespace = {"__file__": code.co_filename}
-        exec(code, namespace)
-        if extra_namespace:
-            namespace.update(extra_namespace)
-        rv = cls._from_namespace(schema, namespace)
-        # rv._uptodate = uptodate
-        return rv
-
-    @classmethod
-    def from_module_dict(cls, schema, module_dict):
-        # type: (GraphQLSchema, Dict[str, Any]) -> GraphQLCompiledDocument
-        """Creates a template object from a module.  This is used by the
-        module loader to create a document object.
-        """
-        return cls._from_namespace(schema, module_dict)
-
-    @classmethod
-    def _from_namespace(cls, schema, namespace):
-        # type: (GraphQLSchema, Dict[str, Any]) -> GraphQLCompiledDocument
-        document_string = namespace.get("document_string", "")  # type: str
-        document_ast = namespace.get("document_ast")  # type: ignore
-        execute = namespace["execute"]  # type: Callable
-
-        namespace["schema"] = schema
-        return cls(  # type: ignore
-            schema=schema,
-            document_string=document_string,
-            document_ast=document_ast,
-            execute=execute,
-        )
diff --git a/graphql/backend/core.py b/graphql/backend/core.py
deleted file mode 100644
index 41aea2d..0000000
--- a/graphql/backend/core.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from functools import partial
-from six import string_types
-
-from ..execution import execute, ExecutionResult
-from ..language.base import parse, print_ast
-from ..language import ast
-from ..validation import validate
-
-from .base import GraphQLBackend, GraphQLDocument
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, Optional, Union
-    from ..language.ast import Document
-    from ..type.schema import GraphQLSchema
-    from rx import Observable
-
-
-def execute_and_validate(
-    schema,  # type: GraphQLSchema
-    document_ast,  # type: Document
-    *args,  # type: Any
-    **kwargs  # type: Any
-):
-    # type: (...) -> Union[ExecutionResult, Observable]
-    do_validation = kwargs.get("validate", True)
-    if do_validation:
-        validation_errors = validate(schema, document_ast)
-        if validation_errors:
-            return ExecutionResult(errors=validation_errors, invalid=True)
-
-    return execute(schema, document_ast, *args, **kwargs)
-
-
-class GraphQLCoreBackend(GraphQLBackend):
-    """GraphQLCoreBackend will return a document using the default
-    graphql executor"""
-
-    def __init__(self, executor=None):
-        # type: (Optional[Any]) -> None
-        self.execute_params = {"executor": executor}
-
-    def document_from_string(self, schema, document_string):
-        # type: (GraphQLSchema, Union[Document, str]) -> GraphQLDocument
-        if isinstance(document_string, ast.Document):
-            document_ast = document_string
-            document_string = print_ast(document_ast)
-        else:
-            assert isinstance(
-                document_string, string_types
-            ), "The query must be a string"
-            document_ast = parse(document_string)
-        return GraphQLDocument(
-            schema=schema,
-            document_string=document_string,
-            document_ast=document_ast,
-            execute=partial(
-                execute_and_validate, schema, document_ast, **self.execute_params
-            ),
-        )
diff --git a/graphql/backend/decider.py b/graphql/backend/decider.py
deleted file mode 100644
index 5fdd39b..0000000
--- a/graphql/backend/decider.py
+++ /dev/null
@@ -1,211 +0,0 @@
-import atexit
-import logging
-import threading
-import os
-from time import sleep, time
-
-
-from .base import GraphQLBackend, GraphQLDocument
-from .cache import GraphQLCachedBackend
-from ..pyutils.compat import Queue, check_threads
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import List, Union, Optional, Hashable, Dict, Tuple, Type
-    from ..type.schema import GraphQLSchema
-
-
-DEFAULT_TIMEOUT = 10
-
-logger = logging.getLogger("graphql.errors")
-
-
-# Code shamelessly taken from
-# https://github.com/getsentry/raven-python/blob/master/raven/transport/threaded.py
-# Why to create when we can take something that works?
-# Attributions to the Sentry team :)
-class AsyncWorker(object):
-    _terminator = object()
-
-    def __init__(self, shutdown_timeout=DEFAULT_TIMEOUT):
-        check_threads()
-        self._queue = Queue(-1)
-        self._lock = threading.Lock()
-        self._thread = None
-        self._thread_for_pid = None
-        self.options = {"shutdown_timeout": shutdown_timeout}
-        self.start()
-
-    def is_alive(self):
-        if self._thread_for_pid != os.getpid():
-            return False
-        return self._thread and self._thread.is_alive()
-
-    def _ensure_thread(self):
-        if self.is_alive():
-            return
-        self.start()
-
-    def main_thread_terminated(self):
-        with self._lock:
-            if not self.is_alive():
-                # thread not started or already stopped - nothing to do
-                return
-
-            # wake the processing thread up
-            self._queue.put_nowait(self._terminator)
-
-            timeout = self.options["shutdown_timeout"]
-
-            # wait briefly, initially
-            initial_timeout = min(0.1, timeout)
-
-            if not self._timed_queue_join(initial_timeout):
-                # if that didn't work, wait a bit longer
-                # NB that size is an approximation, because other threads may
-                # add or remove items
-                size = self._queue.qsize()
-
-                print("GraphQL is attempting to retrieve %i pending documents" % size)
-                print("Waiting up to %s seconds" % timeout)
-
-                if os.name == "nt":
-                    print("Press Ctrl-Break to quit")
-                else:
-                    print("Press Ctrl-C to quit")
-
-                self._timed_queue_join(timeout - initial_timeout)
-
-            self._thread = None
-
-    def _timed_queue_join(self, timeout):
-        """
-        implementation of Queue.join which takes a 'timeout' argument
-        returns true on success, false on timeout
-        """
-        deadline = time() + timeout
-        queue = self._queue
-
-        queue.all_tasks_done.acquire()
-        try:
-            while queue.unfinished_tasks:
-                delay = deadline - time()
-                if delay <= 0:
-                    # timed out
-                    return False
-
-                queue.all_tasks_done.wait(timeout=delay)
-
-            return True
-
-        finally:
-            queue.all_tasks_done.release()
-
-    def start(self):
-        """
-        Starts the task thread.
-        """
-        self._lock.acquire()
-        try:
-            if not self.is_alive():
-                self._thread = threading.Thread(
-                    target=self._target, name="graphql.AsyncWorker"
-                )
-                self._thread.setDaemon(True)
-                self._thread.start()
-                self._thread_for_pid = os.getpid()
-        finally:
-            self._lock.release()
-            atexit.register(self.main_thread_terminated)
-
-    def stop(self, timeout=None):
-        """
-        Stops the task thread. Synchronous!
-        """
-        with self._lock:
-            if self._thread:
-                self._queue.put_nowait(self._terminator)
-                self._thread.join(timeout=timeout)
-                self._thread = None
-                self._thread_for_pid = None
-
-    def queue(self, callback, *args, **kwargs):
-        self._ensure_thread()
-        self._queue.put_nowait((callback, args, kwargs))
-
-    def _target(self):
-        while True:
-            record = self._queue.get()
-            try:
-                if record is self._terminator:
-                    break
-                callback, args, kwargs = record
-                try:
-                    callback(*args, **kwargs)
-                except Exception:
-                    logger.error("Failed processing job", exc_info=True)
-            finally:
-                self._queue.task_done()
-
-            sleep(0)
-
-
-class GraphQLDeciderBackend(GraphQLCachedBackend):
-    """GraphQLDeciderBackend will offload the document generation to the
-    main backend in a new thread, serving meanwhile the document from the fallback
-    backend"""
-
-    _worker = None
-    fallback_backend = None  # type: GraphQLBackend
-    # _in_queue = object()
-
-    def __init__(
-        self,
-        backend,  # type: Union[List[GraphQLBackend], Tuple[GraphQLBackend, GraphQLBackend], GraphQLBackend]
-        fallback_backend=None,  # type: Optional[GraphQLBackend]
-        cache_map=None,  # type: Optional[Dict[Hashable, GraphQLDocument]]
-        use_consistent_hash=False,  # type: bool
-        worker_class=AsyncWorker,  # type: Type[AsyncWorker]
-    ):
-        # type: (...) -> None
-        if not backend:
-            raise Exception("Need to provide backends to decide into.")
-        if isinstance(backend, (list, tuple)):
-            if fallback_backend:
-                raise Exception("Can't set a fallback backend and backends as array")
-            if len(backend) != 2:
-                raise Exception("Only two backends are supported for now")
-            backend, fallback_backend = backend[0], backend[1]  # type: ignore
-        else:
-            if not fallback_backend:
-                raise Exception("Need to provide a fallback backend")
-
-        self.fallback_backend = fallback_backend  # type: ignore
-        self.worker_class = worker_class
-        super(GraphQLDeciderBackend, self).__init__(
-            backend, cache_map=cache_map, use_consistent_hash=use_consistent_hash
-        )
-
-    def queue_backend(self, key, schema, request_string):
-        # type: (Hashable, GraphQLSchema, str) -> None
-        self.cache_map[key] = self.backend.document_from_string(schema, request_string)
-
-    def get_worker(self):
-        # type: () -> AsyncWorker
-        if self._worker is None or not self._worker.is_alive():
-            self._worker = self.worker_class()
-        return self._worker
-
-    def document_from_string(self, schema, request_string):
-        # type: (GraphQLSchema, str) -> GraphQLDocument
-        """This method returns a GraphQLQuery (from cache if present)"""
-        key = self.get_key_for_schema_and_document_string(schema, request_string)
-        if key not in self.cache_map:
-            # We return from the fallback
-            self.cache_map[key] = self.fallback_backend.document_from_string(
-                schema, request_string
-            )
-            # We ensure the main backend response is in the queue
-            self.get_worker().queue(self.queue_backend, key, schema, request_string)
-
-        return self.cache_map[key]
diff --git a/graphql/backend/quiver_cloud.py b/graphql/backend/quiver_cloud.py
deleted file mode 100644
index 2c6796b..0000000
--- a/graphql/backend/quiver_cloud.py
+++ /dev/null
@@ -1,105 +0,0 @@
-try:
-    import requests
-except ImportError:
-    raise ImportError(
-        "requests package is required for Quiver Cloud backend.\n"
-        "You can install it using: pip install requests"
-    )
-
-from ..utils.schema_printer import print_schema
-
-from .base import GraphQLBackend
-from .compiled import GraphQLCompiledDocument
-
-from six.moves.urllib.parse import urlparse
-
-GRAPHQL_QUERY = """
-mutation($schemaDsl: String!, $query: String!, $pythonOptions: PythonOptions) {
-  generateCode(
-    schemaDsl: $schemaDsl
-    query: $query,
-    language: PYTHON,
-    pythonOptions: $pythonOptions
-  ) {
-    code
-    compilationTime
-    errors {
-      type
-    }
-  }
-}
-"""
-
-
-class GraphQLQuiverCloudBackend(GraphQLBackend):
-    def __init__(self, dsn, python_options=None, **options):
-        super(GraphQLQuiverCloudBackend, self).__init__(**options)
-        try:
-            url = urlparse(dsn.strip())
-        except Exception:
-            raise Exception("Received wrong url {}".format(dsn))
-
-        netloc = url.hostname
-        if url.port:
-            netloc += ":%s" % url.port
-
-        path_bits = url.path.rsplit("/", 1)
-        if len(path_bits) > 1:
-            path = path_bits[0]
-        else:
-            path = ""
-
-        self.api_url = "{}://{}{}".format(url.scheme.rsplit("+", 1)[-1], netloc, path)
-        self.public_key = url.username
-        self.secret_key = url.password
-        self.extra_namespace = {}
-        if python_options is None:
-            python_options = {"asyncFramework": "PROMISE"}
-        wait_for_promises = python_options.pop("wait_for_promises", None)
-        if wait_for_promises:
-            assert callable(wait_for_promises), "wait_for_promises must be callable."
-            self.extra_namespace["wait_for_promises"] = wait_for_promises
-        self.python_options = python_options
-
-    def make_post_request(self, url, auth, json_payload):
-        """This function executes the request with the provided
-        json payload and return the json response"""
-        response = requests.post(url, auth=auth, json=json_payload)
-        return response.json()
-
-    def generate_source(self, schema, query):
-        variables = {
-            "schemaDsl": print_schema(schema),
-            "query": query,
-            "pythonOptions": self.python_options,
-        }
-
-        json_response = self.make_post_request(
-            "{}/graphql".format(self.api_url),
-            auth=(self.public_key, self.secret_key),
-            json_payload={"query": GRAPHQL_QUERY, "variables": variables},
-        )
-
-        errors = json_response.get("errors")
-        if errors:
-            raise Exception(errors[0].get("message"))
-        data = json_response.get("data", {})
-        code_generation = data.get("generateCode", {})
-        code = code_generation.get("code")
-        if not code:
-            raise Exception("Cant get the code. Received json from Quiver Cloud")
-        code = str(code)
-        return code
-
-    def document_from_string(self, schema, request_string):
-        source = self.generate_source(schema, request_string)
-        filename = "<document>"
-        code = compile(source, filename, "exec")
-
-        def uptodate():
-            return True
-
-        document = GraphQLCompiledDocument.from_code(
-            schema, code, uptodate, self.extra_namespace
-        )
-        return document
diff --git a/graphql/backend/tests/__init__.py b/graphql/backend/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/backend/tests/schema.py b/graphql/backend/tests/schema.py
deleted file mode 100644
index 00a7f93..0000000
--- a/graphql/backend/tests/schema.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
-
-
-Query = GraphQLObjectType(
-    "Query", lambda: {"hello": GraphQLField(GraphQLString, resolver=lambda *_: "World")}
-)
-
-schema = GraphQLSchema(Query)
diff --git a/graphql/backend/tests/test_base.py b/graphql/backend/tests/test_base.py
deleted file mode 100644
index c9a8dfd..0000000
--- a/graphql/backend/tests/test_base.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import pytest
-from .. import get_default_backend, set_default_backend, GraphQLCoreBackend
-
-
-def test_get_default_backend_returns_core_by_default():
-    # type: () -> None
-    backend = get_default_backend()
-    assert isinstance(backend, GraphQLCoreBackend)
-
-
-def test_set_default_backend():
-    # type: () -> None
-    default_backend = get_default_backend()
-    new_backend = GraphQLCoreBackend()
-    assert new_backend != default_backend
-    set_default_backend(new_backend)
-    assert get_default_backend() == new_backend
-
-
-def test_set_default_backend_fails_if_invalid_backend():
-    # type: () -> None
-    default_backend = get_default_backend()
-    with pytest.raises(Exception) as exc_info:
-        set_default_backend(object())
-    assert str(exc_info.value) == "backend must be an instance of GraphQLBackend."
-    assert get_default_backend() == default_backend
diff --git a/graphql/backend/tests/test_cache.py b/graphql/backend/tests/test_cache.py
deleted file mode 100644
index a20a18c..0000000
--- a/graphql/backend/tests/test_cache.py
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""Tests for `graphql.backend.cache` module."""
-
-import pytest
-
-from ..core import GraphQLCoreBackend
-from ..cache import GraphQLCachedBackend
-from graphql.execution.executors.sync import SyncExecutor
-from .schema import schema
-
-
-def test_cached_backend():
-    # type: () -> None
-    cached_backend = GraphQLCachedBackend(GraphQLCoreBackend())
-    document1 = cached_backend.document_from_string(schema, "{ hello }")
-    document2 = cached_backend.document_from_string(schema, "{ hello }")
-    assert document1 == document2
-
-
-def test_cached_backend_with_use_consistent_hash():
-    # type: () -> None
-    cached_backend = GraphQLCachedBackend(
-        GraphQLCoreBackend(), use_consistent_hash=True
-    )
-    document1 = cached_backend.document_from_string(schema, "{ hello }")
-    document2 = cached_backend.document_from_string(schema, "{ hello }")
-    assert document1 == document2
diff --git a/graphql/backend/tests/test_compileddocument.py b/graphql/backend/tests/test_compileddocument.py
deleted file mode 100644
index 787089a..0000000
--- a/graphql/backend/tests/test_compileddocument.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from ...language.base import parse
-from ...utils.ast_to_code import ast_to_code
-from ..compiled import GraphQLCompiledDocument
-from .schema import schema
-
-
-def test_compileddocument_from_module_dict():
-    # type: () -> None
-    document_string = "{ hello }"
-    document_ast = parse(document_string)
-    document = GraphQLCompiledDocument.from_module_dict(
-        schema,
-        {
-            "document_string": document_string,
-            "document_ast": document_ast,
-            "execute": lambda *_: True,
-        },
-    )
-    assert document.operations_map == {None: "query"}
-    assert document.document_string == document_string
-    assert document.document_ast == document_ast
-    assert document.schema == schema
-    assert document.execute()
-
-
-def test_compileddocument_from_code():
-    # type: () -> None
-    document_string = "{ hello }"
-    document_ast = parse(document_string)
-    code = '''
-# -*- coding: utf-8 -*-
-from __future__ import unicode_literals
-
-from graphql.language import ast
-from graphql.language.parser import Loc
-from graphql.language.source import Source
-
-
-schema = None
-document_string = """{document_string}"""
-source = Source(document_string)
-
-
-def loc(start, end):
-    return Loc(start, end, source)
-
-document_ast = {document_ast}
-
-def execute(*_):
-    return True
-'''.format(
-        document_string=document_string, document_ast=ast_to_code(document_ast)
-    )
-    document = GraphQLCompiledDocument.from_code(schema, code)
-    assert document.operations_map == {None: "query"}
-    assert document.document_string == document_string
-    assert document.document_ast == document_ast
-    assert document.schema == schema
-    assert document.execute()
diff --git a/graphql/backend/tests/test_core.py b/graphql/backend/tests/test_core.py
deleted file mode 100644
index 257eb64..0000000
--- a/graphql/backend/tests/test_core.py
+++ /dev/null
@@ -1,54 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""Tests for `graphql.backend.core` module."""
-
-import pytest
-from graphql.execution.executors.sync import SyncExecutor
-
-from ..base import GraphQLBackend, GraphQLDocument
-from ..core import GraphQLCoreBackend
-from .schema import schema
-
-if False:
-    from typing import Any
-
-
-def test_core_backend():
-    # type: () -> None
-    """Sample pytest test function with the pytest fixture as an argument."""
-    backend = GraphQLCoreBackend()
-    assert isinstance(backend, GraphQLBackend)
-    document = backend.document_from_string(schema, "{ hello }")
-    assert isinstance(document, GraphQLDocument)
-    result = document.execute()
-    assert not result.errors
-    assert result.data == {"hello": "World"}
-
-
-def test_backend_is_not_cached_by_default():
-    # type: () -> None
-    """Sample pytest test function with the pytest fixture as an argument."""
-    backend = GraphQLCoreBackend()
-    document1 = backend.document_from_string(schema, "{ hello }")
-    document2 = backend.document_from_string(schema, "{ hello }")
-    assert document1 != document2
-
-
-class BaseExecutor(SyncExecutor):
-    executed = False
-
-    def execute(self, *args, **kwargs):
-        # type: (*Any, **Any) -> str
-        self.executed = True
-        return super(BaseExecutor, self).execute(*args, **kwargs)
-
-
-def test_backend_can_execute_custom_executor():
-    # type: () -> None
-    executor = BaseExecutor()
-    backend = GraphQLCoreBackend(executor=executor)
-    document1 = backend.document_from_string(schema, "{ hello }")
-    result = document1.execute()
-    assert not result.errors
-    assert result.data == {"hello": "World"}
-    assert executor.executed
diff --git a/graphql/backend/tests/test_decider.py b/graphql/backend/tests/test_decider.py
deleted file mode 100644
index 32dccae..0000000
--- a/graphql/backend/tests/test_decider.py
+++ /dev/null
@@ -1,124 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-"""Tests for `graphql.backend.decider` module."""
-
-import pytest
-from threading import Event
-
-from ..base import GraphQLBackend, GraphQLDocument
-from ..core import GraphQLCoreBackend
-from ..cache import GraphQLCachedBackend
-from ..decider import GraphQLDeciderBackend
-
-from .schema import schema
-
-if False:
-    from typing import Any
-
-
-class FakeBackend(GraphQLBackend):
-    def __init__(self, name, raises=False):
-        # type: (str, bool) -> None
-        self.raises = raises
-        self.name = name
-        self.event = Event()
-
-    @property
-    def reached(self):
-        return self.event.is_set()
-
-    def document_from_string(self, *args, **kwargs):
-        # type: (*Any, **Any) -> str
-        self.event.set()
-        if self.raises:
-            raise Exception("Backend failed")
-        return self.name
-
-    def wait(self):
-        return self.event.wait()
-
-    def reset(self):
-        # type: () -> None
-        self.event = Event()
-
-
-def test_decider_backend_healthy_backend():
-    # type: () -> None
-    backend1 = FakeBackend(name="main")
-    backend2 = FakeBackend(name="fallback")
-    decider_backend = GraphQLDeciderBackend(backend1, backend2)
-
-    document = decider_backend.document_from_string(schema, "{ hello }")
-    assert not backend1.reached
-    assert backend2.reached
-    assert document == "fallback"
-
-    backend1.wait()
-    backend1.reset()
-    backend2.reset()
-    document = decider_backend.document_from_string(schema, "{ hello }")
-    assert not backend1.reached
-    assert not backend2.reached
-    assert document == "main"
-
-
-def test_decider_backend_unhealthy_backend():
-    # type: () -> None
-    backend1 = FakeBackend(name="main", raises=True)
-    backend2 = FakeBackend(name="fallback")
-    decider_backend = GraphQLDeciderBackend(backend1, backend2)
-
-    document = decider_backend.document_from_string(schema, "{ hello }")
-    assert not backend1.reached
-    assert backend2.reached
-    assert document == "fallback"
-
-    backend1.wait()
-    backend1.reset()
-    backend2.reset()
-    document = decider_backend.document_from_string(schema, "{ hello }")
-
-    assert document == "fallback"
-    assert not backend1.reached
-    assert not backend2.reached
-
-
-def test_decider_old_syntax():
-    # type: () -> None
-    backend1 = FakeBackend(name="main", raises=True)
-    backend2 = FakeBackend(name="fallback")
-    decider_backend = GraphQLDeciderBackend([backend1, backend2])
-    assert decider_backend.backend is backend1
-    assert decider_backend.fallback_backend is backend2
-
-
-# def test_decider_backend_dont_use_cache():
-#     # type: () -> None
-#     backend1 = FakeBackend()
-#     backend2 = FakeBackend()
-#     decider_backend = GraphQLDeciderBackend([backend1, backend2])
-
-#     decider_backend.document_from_string(schema, "{ hello }")
-#     assert backend1.reached
-#     assert not backend2.reached
-
-#     backend1.reset()
-#     decider_backend.document_from_string(schema, "{ hello }")
-#     assert backend1.reached
-
-
-# def test_decider_backend_use_cache_if_provided():
-#     # type: () -> None
-#     backend1 = FakeBackend()
-#     backend2 = FakeBackend()
-#     decider_backend = GraphQLDeciderBackend(
-#         [GraphQLCachedBackend(backend1), GraphQLCachedBackend(backend2)]
-#     )
-
-#     decider_backend.document_from_string(schema, "{ hello }")
-#     assert backend1.reached
-#     assert not backend2.reached
-
-#     backend1.reset()
-#     decider_backend.document_from_string(schema, "{ hello }")
-#     assert not backend1.reached
diff --git a/graphql/backend/tests/test_document.py b/graphql/backend/tests/test_document.py
deleted file mode 100644
index e3b74c9..0000000
--- a/graphql/backend/tests/test_document.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from ...language.base import parse
-from ..base import GraphQLDocument
-from .schema import schema
-from graphql.backend.base import GraphQLDocument
-
-
-def create_document(document_string):
-    # type: (str) -> GraphQLDocument
-    document_ast = parse(document_string)
-    return GraphQLDocument(
-        schema=schema,
-        document_string=document_string,
-        document_ast=document_ast,
-        execute=lambda *_: None,
-    )
-
-
-def test_document_operations_map_unnamed_operation():
-    # type: () -> None
-    document = create_document("{ hello }")
-    assert document.operations_map == {None: "query"}
-
-
-def test_document_operations_map_multiple_queries():
-    document = create_document(
-        """
-    query MyQuery1 { hello }
-    query MyQuery2 { hello }
-    """
-    )
-    assert document.operations_map == {"MyQuery1": "query", "MyQuery2": "query"}
-
-
-def test_document_operations_map_multiple_queries():
-    # type: () -> None
-    document = create_document(
-        """
-    query MyQuery { hello }
-    mutation MyMutation { hello }
-    subscription MySubscription { hello }
-    """
-    )
-    assert document.operations_map == {
-        "MyQuery": "query",
-        "MyMutation": "mutation",
-        "MySubscription": "subscription",
-    }
-
-
-def test_document_get_operation_type_unnamed_operation():
-    # type: () -> None
-    document = create_document(
-        """
-    query { hello }
-    """
-    )
-    assert document.get_operation_type(None) == "query"
-    assert document.get_operation_type("Unknown") is None
-
-
-def test_document_get_operation_type_multiple_operations():
-    # type: () -> None
-    document = create_document(
-        """
-    query MyQuery { hello }
-    mutation MyMutation {hello}
-    """
-    )
-    assert document.get_operation_type(None) is None
-    assert document.get_operation_type("MyQuery") == "query"
-    assert document.get_operation_type("MyMutation") == "mutation"
-    assert document.get_operation_type("Unexistent") is None
-
-
-def test_document_get_operation_type_multiple_operations_empty_operation_name():
-    # type: () -> None
-    document = create_document(
-        """
-    query MyQuery { hello }
-    mutation {hello}
-    """
-    )
-    assert document.get_operation_type(None) is "mutation"
diff --git a/graphql/error/__init__.py b/graphql/error/__init__.py
deleted file mode 100644
index 7a62771..0000000
--- a/graphql/error/__init__.py
+++ /dev/null
@@ -1,6 +0,0 @@
-from .base import GraphQLError
-from .located_error import GraphQLLocatedError
-from .syntax_error import GraphQLSyntaxError
-from .format_error import format_error
-
-__all__ = ["GraphQLError", "GraphQLLocatedError", "GraphQLSyntaxError", "format_error"]
diff --git a/graphql/error/base.py b/graphql/error/base.py
deleted file mode 100644
index 0aae96b..0000000
--- a/graphql/error/base.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import six
-from ..language.location import get_location
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.source import Source
-    from ..language.location import SourceLocation
-    from types import TracebackType
-    from typing import Dict, Optional, List, Any, Union
-
-
-class GraphQLError(Exception):
-    __slots__ = (
-        "message",
-        "nodes",
-        "stack",
-        "original_error",
-        "_source",
-        "_positions",
-        "_locations",
-        "path",
-        "extensions",
-    )
-
-    def __init__(
-        self,
-        message,  # type: str
-        nodes=None,  # type: Any
-        stack=None,  # type: Optional[TracebackType]
-        source=None,  # type: Optional[Any]
-        positions=None,  # type: Optional[Any]
-        locations=None,  # type: Optional[Any]
-        path=None,  # type: Union[List[Union[int, str]], List[str], None]
-        extensions=None,  # type: Optional[Dict[str, Any]]
-    ):
-        # type: (...) -> None
-        super(GraphQLError, self).__init__(message)
-        self.message = message
-        self.nodes = nodes
-        self.stack = stack
-        self._source = source
-        self._positions = positions
-        self._locations = locations
-        self.path = path
-        self.extensions = extensions
-        return None
-
-    @property
-    def source(self):
-        # type: () -> Optional[Source]
-        if self._source:
-            return self._source
-        if self.nodes:
-            node = self.nodes[0]
-            return node and node.loc and node.loc.source
-        return None
-
-    @property
-    def positions(self):
-        # type: () -> Optional[List[int]]
-        if self._positions:
-            return self._positions
-        if self.nodes is not None:
-            node_positions = [node.loc and node.loc.start for node in self.nodes]
-            if any(node_positions):
-                return node_positions
-        return None
-
-    def reraise(self):
-        # type: () -> None
-        if self.stack:
-            six.reraise(type(self), self, self.stack)
-        else:
-            raise self
-
-    @property
-    def locations(self):
-        # type: () -> Optional[List[SourceLocation]]
-        if not self._locations:
-            source = self.source
-            if self.positions and source:
-                self._locations = [get_location(source, pos) for pos in self.positions]
-        return self._locations
diff --git a/graphql/error/format_error.py b/graphql/error/format_error.py
deleted file mode 100644
index adafd85..0000000
--- a/graphql/error/format_error.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from .base import GraphQLError
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, Dict
-
-
-def format_error(error):
-    # type: (Exception) -> Dict[str, Any]
-    # Protect against UnicodeEncodeError when run in py2 (#216)
-    try:
-        message = str(error)
-    except UnicodeEncodeError:
-        message = error.message.encode("utf-8")  # type: ignore
-    formatted_error = {"message": message}  # type: Dict[str, Any]
-    if isinstance(error, GraphQLError):
-        if error.locations is not None:
-            formatted_error["locations"] = [
-                {"line": loc.line, "column": loc.column} for loc in error.locations
-            ]
-        if error.path is not None:
-            formatted_error["path"] = error.path
-
-        if error.extensions is not None:
-            formatted_error["extensions"] = error.extensions
-
-    return formatted_error
diff --git a/graphql/error/located_error.py b/graphql/error/located_error.py
deleted file mode 100644
index 3fe55a4..0000000
--- a/graphql/error/located_error.py
+++ /dev/null
@@ -1,45 +0,0 @@
-import sys
-
-from .base import GraphQLError
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.ast import Field
-    from typing import List, Union
-
-__all__ = ["GraphQLLocatedError"]
-
-
-class GraphQLLocatedError(GraphQLError):
-    def __init__(
-        self,
-        nodes,  # type: List[Field]
-        original_error=None,  # type: Exception
-        path=None,  # type: Union[List[Union[int, str]], List[str]]
-    ):
-        # type: (...) -> None
-        if original_error:
-            try:
-                message = str(original_error)
-            except UnicodeEncodeError:
-                message = original_error.message.encode("utf-8")  # type: ignore
-        else:
-            message = "An unknown error occurred."
-
-        stack = (
-            original_error
-            and (
-                getattr(original_error, "stack", None)
-                # unfortunately, this is only available in Python 3:
-                or getattr(original_error, "__traceback__", None)
-            )
-            or sys.exc_info()[2]
-        )
-
-        extensions = (
-            getattr(original_error, "extensions", None) if original_error else None
-        )
-        super(GraphQLLocatedError, self).__init__(
-            message=message, nodes=nodes, stack=stack, path=path, extensions=extensions
-        )
-        self.original_error = original_error
diff --git a/graphql/error/syntax_error.py b/graphql/error/syntax_error.py
deleted file mode 100644
index d38adc1..0000000
--- a/graphql/error/syntax_error.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from ..language.location import get_location
-from .base import GraphQLError
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.source import Source
-    from ..language.location import SourceLocation
-
-__all__ = ["GraphQLSyntaxError"]
-
-
-class GraphQLSyntaxError(GraphQLError):
-    def __init__(self, source, position, description):
-        # type: (Source, int, str) -> None
-        location = get_location(source, position)
-        super(GraphQLSyntaxError, self).__init__(
-            message=u"Syntax Error {} ({}:{}) {}\n\n{}".format(
-                source.name,
-                location.line,
-                location.column,
-                description,
-                highlight_source_at_location(source, location),
-            ),
-            source=source,
-            positions=[position],
-        )
-
-
-def highlight_source_at_location(source, location):
-    # type: (Source, SourceLocation) -> str
-    line = location.line
-    lines = source.body.splitlines()
-    pad_len = len(str(line + 1))
-    result = u""
-    format = (u"{:>" + str(pad_len) + "}: {}\n").format
-    if line >= 2:
-        result += format(line - 1, lines[line - 2])
-    if line <= len(lines):
-        result += format(line, lines[line - 1])
-        result += " " * (1 + pad_len + location.column) + "^\n"
-    if line < len(lines):
-        result += format(line + 1, lines[line])
-    return result
diff --git a/graphql/error/tests/__init__.py b/graphql/error/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/error/tests/test_base.py b/graphql/error/tests/test_base.py
deleted file mode 100644
index 3ddc091..0000000
--- a/graphql/error/tests/test_base.py
+++ /dev/null
@@ -1,101 +0,0 @@
-import sys
-
-import pytest
-import traceback
-
-from promise import Promise
-
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from graphql.execution.base import ResolveInfo
-    from typing import Any
-    from typing import Optional
-
-
-def test_raise():
-    # type: () -> None
-    ast = parse("query Example { a }")
-
-    def resolver(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> None
-        raise Exception("Failed")
-
-    Type = GraphQLObjectType(
-        "Type", {"a": GraphQLField(GraphQLString, resolver=resolver)}
-    )
-
-    result = execute(GraphQLSchema(Type), ast)
-    assert str(result.errors[0]) == "Failed"
-
-
-def test_reraise():
-    # type: () -> None
-    ast = parse("query Example { a }")
-
-    def resolver(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> None
-        raise Exception("Failed")
-
-    Type = GraphQLObjectType(
-        "Type", {"a": GraphQLField(GraphQLString, resolver=resolver)}
-    )
-
-    result = execute(GraphQLSchema(Type), ast)
-    with pytest.raises(Exception) as exc_info:
-        result.errors[0].reraise()
-
-    extracted = traceback.extract_tb(exc_info.tb)
-    formatted_tb = [row[2:] for row in extracted]
-    formatted_tb = [tb for tb in formatted_tb if tb[0] != "reraise"]
-
-    assert formatted_tb == [
-        ("test_reraise", "result.errors[0].reraise()"),
-        (
-            "resolve_or_error",
-            "return executor.execute(resolve_fn, source, info, **args)",
-        ),
-        ("execute", "return fn(*args, **kwargs)"),
-        ("resolver", 'raise Exception("Failed")'),
-    ]
-
-    assert str(exc_info.value) == "Failed"
-
-
-@pytest.mark.skipif(sys.version_info < (3,), reason="this works only with Python 3")
-def test_reraise_from_promise():
-    # type: () -> None
-    ast = parse("query Example { a }")
-
-    def fail():
-        raise Exception("Failed")
-
-    def resolver(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> None
-        return Promise(lambda resolve, reject: resolve(fail()))
-
-    Type = GraphQLObjectType(
-        "Type", {"a": GraphQLField(GraphQLString, resolver=resolver)}
-    )
-
-    result = execute(GraphQLSchema(Type), ast)
-    with pytest.raises(Exception) as exc_info:
-        result.errors[0].reraise()
-
-    extracted = traceback.extract_tb(exc_info.tb)
-    formatted_tb = [row[2:] for row in extracted]
-    formatted_tb = [tb for tb in formatted_tb if tb[0] != "reraise"]
-
-    print(formatted_tb)
-
-    assert formatted_tb == [
-        ("test_reraise_from_promise", "result.errors[0].reraise()"),
-        ("_resolve_from_executor", "executor(resolve, reject)"),
-        ("<lambda>", "return Promise(lambda resolve, reject: resolve(fail()))"),
-        ("fail", 'raise Exception("Failed")'),
-    ]
-
-    assert str(exc_info.value) == "Failed"
diff --git a/graphql/execution/__init__.py b/graphql/execution/__init__.py
deleted file mode 100644
index d6c2a7f..0000000
--- a/graphql/execution/__init__.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Terminology
-
-"Definitions" are the generic name for top-level statements in the document.
-Examples of this include:
-1) Operations (such as a query)
-2) Fragments
-
-"Operations" are a generic name for requests in the document.
-Examples of this include:
-1) query,
-2) mutation
-
-"Selections" are the statements that can appear legally and at
-single level of the query. These include:
-1) field references e.g "a"
-2) fragment "spreads" e.g. "...c"
-3) inline fragment "spreads" e.g. "...on Type { a }"
-"""
-from .executor import execute, subscribe
-from .base import ExecutionResult, ResolveInfo
-from .middleware import middlewares, MiddlewareManager
-
-
-__all__ = [
-    "execute",
-    "subscribe",
-    "ExecutionResult",
-    "ResolveInfo",
-    "MiddlewareManager",
-    "middlewares",
-]
diff --git a/graphql/execution/base.py b/graphql/execution/base.py
deleted file mode 100644
index d71151a..0000000
--- a/graphql/execution/base.py
+++ /dev/null
@@ -1,121 +0,0 @@
-# We keep the following imports to preserve compatibility
-from .utils import (
-    ExecutionContext,
-    SubscriberExecutionContext,
-    get_operation_root_type,
-    collect_fields,
-    should_include_node,
-    does_fragment_condition_match,
-    get_field_entry_key,
-    default_resolve_fn,
-    get_field_def,
-)
-from ..pyutils.ordereddict import OrderedDict
-from ..error.format_error import format_error as default_format_error
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, Optional, Dict, List, Union, Callable, Type
-    from ..language.ast import Field, OperationDefinition
-    from ..type.definition import GraphQLList, GraphQLObjectType, GraphQLScalarType
-    from ..type.schema import GraphQLSchema
-
-
-class ExecutionResult(object):
-    """The result of execution. `data` is the result of executing the
-    query, `errors` is null if no errors occurred, and is a
-    non-empty array if an error occurred."""
-
-    __slots__ = "data", "errors", "invalid", "extensions"
-
-    def __init__(self, data=None, errors=None, invalid=False, extensions=None):
-        # type: (Optional[Dict], Optional[List[Exception]], bool, Optional[Any]) -> None
-        self.data = data
-        self.errors = errors
-        self.extensions = extensions or dict()
-
-        if invalid:
-            assert data is None
-
-        self.invalid = invalid
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, ExecutionResult)
-            and self.data == other.data
-            and self.errors == other.errors
-            and self.invalid == other.invalid
-        )
-
-    def to_dict(self, format_error=None, dict_class=OrderedDict):
-        # type: (Optional[Callable[[Exception], Dict]], Type[Dict]) -> Dict[str, Any]
-        if format_error is None:
-            format_error = default_format_error
-
-        response = dict_class()
-        if self.errors:
-            response["errors"] = [format_error(e) for e in self.errors]
-
-        if not self.invalid:
-            response["data"] = self.data
-
-        return response
-
-
-class ResolveInfo(object):
-    __slots__ = (
-        "field_name",
-        "field_asts",
-        "return_type",
-        "parent_type",
-        "schema",
-        "fragments",
-        "root_value",
-        "operation",
-        "variable_values",
-        "context",
-        "path",
-    )
-
-    def __init__(
-        self,
-        field_name,  # type: str
-        field_asts,  # type: List[Field]
-        return_type,  # type: Union[GraphQLList, GraphQLObjectType, GraphQLScalarType]
-        parent_type,  # type: GraphQLObjectType
-        schema,  # type: GraphQLSchema
-        fragments,  # type: Dict
-        root_value,  # type: Optional[type]
-        operation,  # type: OperationDefinition
-        variable_values,  # type: Dict
-        context,  # type: Optional[Any]
-        path=None,  # type: Union[List[Union[int, str]], List[str]]
-    ):
-        # type: (...) -> None
-        self.field_name = field_name
-        self.field_asts = field_asts
-        self.return_type = return_type
-        self.parent_type = parent_type
-        self.schema = schema
-        self.fragments = fragments
-        self.root_value = root_value
-        self.operation = operation
-        self.variable_values = variable_values
-        self.context = context
-        self.path = path
-
-
-__all__ = [
-    "ExecutionResult",
-    "ResolveInfo",
-    "ExecutionContext",
-    "SubscriberExecutionContext",
-    "get_operation_root_type",
-    "collect_fields",
-    "should_include_node",
-    "does_fragment_condition_match",
-    "get_field_entry_key",
-    "default_resolve_fn",
-    "get_field_def",
-]
diff --git a/graphql/execution/executor.py b/graphql/execution/executor.py
deleted file mode 100644
index e77050e..0000000
--- a/graphql/execution/executor.py
+++ /dev/null
@@ -1,741 +0,0 @@
-import collections
-
-try:
-    from collections.abc import Iterable
-except ImportError:  # Python < 3.3
-    from collections import Iterable
-import functools
-import logging
-import sys
-import warnings
-from rx import Observable
-
-from six import string_types
-from promise import Promise, promise_for_dict, is_thenable
-
-from ..error import GraphQLError, GraphQLLocatedError
-from ..pyutils.default_ordered_dict import DefaultOrderedDict
-from ..pyutils.ordereddict import OrderedDict
-from ..utils.undefined import Undefined
-from ..type import (
-    GraphQLEnumType,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLSchema,
-    GraphQLUnionType,
-)
-from .base import (
-    ExecutionContext,
-    ExecutionResult,
-    ResolveInfo,
-    collect_fields,
-    default_resolve_fn,
-    get_field_def,
-    get_operation_root_type,
-    SubscriberExecutionContext,
-)
-from .executors.sync import SyncExecutor
-from .middleware import MiddlewareManager
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, Optional, Union, Dict, List, Callable
-    from ..language.ast import Document, OperationDefinition, Field
-
-logger = logging.getLogger(__name__)
-
-
-def subscribe(*args, **kwargs):
-    # type: (*Any, **Any) -> Union[ExecutionResult, Observable]
-    allow_subscriptions = kwargs.pop("allow_subscriptions", True)
-    return execute(  # type: ignore
-        *args, allow_subscriptions=allow_subscriptions, **kwargs
-    )
-
-
-def execute(
-    schema,  # type: GraphQLSchema
-    document_ast,  # type: Document
-    root=None,  # type: Any
-    context=None,  # type: Optional[Any]
-    variables=None,  # type: Optional[Any]
-    operation_name=None,  # type: Optional[str]
-    executor=None,  # type: Any
-    return_promise=False,  # type: bool
-    middleware=None,  # type: Optional[Any]
-    allow_subscriptions=False,  # type: bool
-    **options  # type: Any
-):
-    # type: (...) -> Union[ExecutionResult, Promise[ExecutionResult]]
-
-    if root is None and "root_value" in options:
-        warnings.warn(
-            "root_value has been deprecated. Please use root=... instead.",
-            category=DeprecationWarning,
-            stacklevel=2,
-        )
-        root = options["root_value"]
-    if context is None and "context_value" in options:
-        warnings.warn(
-            "context_value has been deprecated. Please use context=... instead.",
-            category=DeprecationWarning,
-            stacklevel=2,
-        )
-        context = options["context_value"]
-    if variables is None and "variable_values" in options:
-        warnings.warn(
-            "variable_values has been deprecated. Please use variables=... instead.",
-            category=DeprecationWarning,
-            stacklevel=2,
-        )
-        variables = options["variable_values"]
-    assert schema, "Must provide schema"
-    assert isinstance(schema, GraphQLSchema), (
-        "Schema must be an instance of GraphQLSchema. Also ensure that there are "
-        + "not multiple versions of GraphQL installed in your node_modules directory."
-    )
-
-    if middleware:
-        if not isinstance(middleware, MiddlewareManager):
-            middleware = MiddlewareManager(*middleware)
-
-        assert isinstance(middleware, MiddlewareManager), (
-            "middlewares have to be an instance"
-            ' of MiddlewareManager. Received "{}".'.format(middleware)
-        )
-
-    if executor is None:
-        executor = SyncExecutor()
-
-    exe_context = ExecutionContext(
-        schema,
-        document_ast,
-        root,
-        context,
-        variables or {},
-        operation_name,
-        executor,
-        middleware,
-        allow_subscriptions,
-    )
-
-    def promise_executor(v):
-        # type: (Optional[Any]) -> Union[Dict, Promise[Dict], Observable]
-        return execute_operation(exe_context, exe_context.operation, root)
-
-    def on_rejected(error):
-        # type: (Exception) -> None
-        exe_context.errors.append(error)
-        return None
-
-    def on_resolve(data):
-        # type: (Union[None, Dict, Observable]) -> Union[ExecutionResult, Observable]
-        if isinstance(data, Observable):
-            return data
-
-        if not exe_context.errors:
-            return ExecutionResult(data=data)
-
-        return ExecutionResult(data=data, errors=exe_context.errors)
-
-    promise = (
-        Promise.resolve(None).then(promise_executor).catch(on_rejected).then(on_resolve)
-    )
-
-    if not return_promise:
-        exe_context.executor.wait_until_finished()
-        return promise.get()
-    else:
-        clean = getattr(exe_context.executor, "clean", None)
-        if clean:
-            clean()
-
-    return promise
-
-
-def execute_operation(
-    exe_context,  # type: ExecutionContext
-    operation,  # type: OperationDefinition
-    root_value,  # type: Any
-):
-    # type: (...) -> Union[Dict, Promise[Dict]]
-    type = get_operation_root_type(exe_context.schema, operation)
-    fields = collect_fields(
-        exe_context, type, operation.selection_set, DefaultOrderedDict(list), set()
-    )
-
-    if operation.operation == "mutation":
-        return execute_fields_serially(exe_context, type, root_value, [], fields)
-
-    if operation.operation == "subscription":
-        if not exe_context.allow_subscriptions:
-            raise Exception(
-                "Subscriptions are not allowed. "
-                "You will need to either use the subscribe function "
-                "or pass allow_subscriptions=True"
-            )
-        return subscribe_fields(exe_context, type, root_value, fields)
-
-    return execute_fields(exe_context, type, root_value, fields, [], None)
-
-
-def execute_fields_serially(
-    exe_context,  # type: ExecutionContext
-    parent_type,  # type: GraphQLObjectType
-    source_value,  # type: Any
-    path,  # type: List
-    fields,  # type: DefaultOrderedDict
-):
-    # type: (...) -> Promise
-    def execute_field_callback(results, response_name):
-        # type: (Dict, str) -> Union[Dict, Promise[Dict]]
-        field_asts = fields[response_name]
-        result = resolve_field(
-            exe_context,
-            parent_type,
-            source_value,
-            field_asts,
-            None,
-            path + [response_name],
-        )
-        if result is Undefined:
-            return results
-
-        if is_thenable(result):
-
-            def collect_result(resolved_result):
-                # type: (Dict) -> Dict
-                results[response_name] = resolved_result
-                return results
-
-            return result.then(collect_result, None)
-
-        results[response_name] = result
-        return results
-
-    def execute_field(prev_promise, response_name):
-        # type: (Promise, str) -> Promise
-        return prev_promise.then(
-            lambda results: execute_field_callback(results, response_name)
-        )
-
-    return functools.reduce(
-        execute_field, fields.keys(), Promise.resolve(collections.OrderedDict())
-    )
-
-
-def execute_fields(
-    exe_context,  # type: ExecutionContext
-    parent_type,  # type: GraphQLObjectType
-    source_value,  # type: Any
-    fields,  # type: DefaultOrderedDict
-    path,  # type: List[Union[int, str]]
-    info,  # type: Optional[ResolveInfo]
-):
-    # type: (...) -> Union[Dict, Promise[Dict]]
-    contains_promise = False
-
-    final_results = OrderedDict()
-
-    for response_name, field_asts in fields.items():
-        result = resolve_field(
-            exe_context,
-            parent_type,
-            source_value,
-            field_asts,
-            info,
-            path + [response_name],
-        )
-        if result is Undefined:
-            continue
-
-        final_results[response_name] = result
-        if is_thenable(result):
-            contains_promise = True
-
-    if not contains_promise:
-        return final_results
-
-    return promise_for_dict(final_results)
-
-
-def subscribe_fields(
-    exe_context,  # type: ExecutionContext
-    parent_type,  # type: GraphQLObjectType
-    source_value,  # type: Any
-    fields,  # type: DefaultOrderedDict
-):
-    # type: (...) -> Observable
-    subscriber_exe_context = SubscriberExecutionContext(exe_context)
-
-    def on_error(error):
-        subscriber_exe_context.report_error(error)
-
-    def map_result(data):
-        # type: (Dict[str, Any]) -> ExecutionResult
-        if subscriber_exe_context.errors:
-            result = ExecutionResult(data=data, errors=subscriber_exe_context.errors)
-        else:
-            result = ExecutionResult(data=data)
-        subscriber_exe_context.reset()
-        return result
-
-    observables = []  # type: List[Observable]
-
-    # assert len(fields) == 1, "Can only subscribe one element at a time."
-
-    for response_name, field_asts in fields.items():
-        result = subscribe_field(
-            subscriber_exe_context,
-            parent_type,
-            source_value,
-            field_asts,
-            [response_name],
-        )
-        if result is Undefined:
-            continue
-
-        def catch_error(error):
-            subscriber_exe_context.errors.append(error)
-            return Observable.just(None)
-
-        # Map observable results
-        observable = result.catch_exception(catch_error).map(
-            lambda data: map_result({response_name: data})
-        )
-        return observable
-        observables.append(observable)
-
-    return Observable.merge(observables)
-
-
-def resolve_field(
-    exe_context,  # type: ExecutionContext
-    parent_type,  # type: GraphQLObjectType
-    source,  # type: Any
-    field_asts,  # type: List[Field]
-    parent_info,  # type: Optional[ResolveInfo]
-    field_path,  # type: List[Union[int, str]]
-):
-    # type: (...) -> Any
-    field_ast = field_asts[0]
-    field_name = field_ast.name.value
-
-    field_def = get_field_def(exe_context.schema, parent_type, field_name)
-    if not field_def:
-        return Undefined
-
-    return_type = field_def.type
-    resolve_fn = field_def.resolver or default_resolve_fn
-
-    # We wrap the resolve_fn from the middleware
-    resolve_fn_middleware = exe_context.get_field_resolver(resolve_fn)
-
-    # Build a dict of arguments from the field.arguments AST, using the variables scope to
-    # fulfill any variable references.
-    args = exe_context.get_argument_values(field_def, field_ast)
-
-    # The resolve function's optional third argument is a context value that
-    # is provided to every resolve function within an execution. It is commonly
-    # used to represent an authenticated user, or request-specific caches.
-    context = exe_context.context_value
-
-    # The resolve function's optional third argument is a collection of
-    # information about the current execution state.
-    info = ResolveInfo(
-        field_name,
-        field_asts,
-        return_type,
-        parent_type,
-        schema=exe_context.schema,
-        fragments=exe_context.fragments,
-        root_value=exe_context.root_value,
-        operation=exe_context.operation,
-        variable_values=exe_context.variable_values,
-        context=context,
-        path=field_path,
-    )
-
-    executor = exe_context.executor
-    result = resolve_or_error(resolve_fn_middleware, source, info, args, executor)
-
-    return complete_value_catching_error(
-        exe_context, return_type, field_asts, info, field_path, result
-    )
-
-
-def subscribe_field(
-    exe_context,  # type: SubscriberExecutionContext
-    parent_type,  # type: GraphQLObjectType
-    source,  # type: Any
-    field_asts,  # type: List[Field]
-    path,  # type: List[str]
-):
-    # type: (...) -> Observable
-    field_ast = field_asts[0]
-    field_name = field_ast.name.value
-
-    field_def = get_field_def(exe_context.schema, parent_type, field_name)
-    if not field_def:
-        return Undefined
-
-    return_type = field_def.type
-    resolve_fn = field_def.resolver or default_resolve_fn
-
-    # We wrap the resolve_fn from the middleware
-    resolve_fn_middleware = exe_context.get_field_resolver(resolve_fn)
-
-    # Build a dict of arguments from the field.arguments AST, using the variables scope to
-    # fulfill any variable references.
-    args = exe_context.get_argument_values(field_def, field_ast)
-
-    # The resolve function's optional third argument is a context value that
-    # is provided to every resolve function within an execution. It is commonly
-    # used to represent an authenticated user, or request-specific caches.
-    context = exe_context.context_value
-
-    # The resolve function's optional third argument is a collection of
-    # information about the current execution state.
-    info = ResolveInfo(
-        field_name,
-        field_asts,
-        return_type,
-        parent_type,
-        schema=exe_context.schema,
-        fragments=exe_context.fragments,
-        root_value=exe_context.root_value,
-        operation=exe_context.operation,
-        variable_values=exe_context.variable_values,
-        context=context,
-        path=path,
-    )
-
-    executor = exe_context.executor
-    result = resolve_or_error(resolve_fn_middleware, source, info, args, executor)
-
-    if isinstance(result, Exception):
-        raise result
-
-    if not isinstance(result, Observable):
-        raise GraphQLError(
-            "Subscription must return Async Iterable or Observable. Received: {}".format(
-                repr(result)
-            )
-        )
-
-    return result.map(
-        functools.partial(
-            complete_value_catching_error,
-            exe_context,
-            return_type,
-            field_asts,
-            info,
-            path,
-        )
-    )
-
-
-def resolve_or_error(
-    resolve_fn,  # type: Callable
-    source,  # type: Any
-    info,  # type: ResolveInfo
-    args,  # type: Dict
-    executor,  # type: Any
-):
-    # type: (...) -> Any
-    try:
-        return executor.execute(resolve_fn, source, info, **args)
-    except Exception as e:
-        logger.exception(
-            "An error occurred while resolving field {}.{}".format(
-                info.parent_type.name, info.field_name
-            )
-        )
-        e.stack = sys.exc_info()[2]  # type: ignore
-        return e
-
-
-def complete_value_catching_error(
-    exe_context,  # type: ExecutionContext
-    return_type,  # type: Any
-    field_asts,  # type: List[Field]
-    info,  # type: ResolveInfo
-    path,  # type: List[Union[int, str]]
-    result,  # type: Any
-):
-    # type: (...) -> Any
-    # If the field type is non-nullable, then it is resolved without any
-    # protection from errors.
-    if isinstance(return_type, GraphQLNonNull):
-        return complete_value(exe_context, return_type, field_asts, info, path, result)
-
-    # Otherwise, error protection is applied, logging the error and
-    # resolving a null value for this field if one is encountered.
-    try:
-        completed = complete_value(
-            exe_context, return_type, field_asts, info, path, result
-        )
-        if is_thenable(completed):
-
-            def handle_error(error):
-                # type: (Union[GraphQLError, GraphQLLocatedError]) -> Optional[Any]
-                traceback = completed._traceback  # type: ignore
-                exe_context.report_error(error, traceback)
-                return None
-
-            return completed.catch(handle_error)
-
-        return completed
-    except Exception as e:
-        traceback = sys.exc_info()[2]
-        exe_context.report_error(e, traceback)
-        return None
-
-
-def complete_value(
-    exe_context,  # type: ExecutionContext
-    return_type,  # type: Any
-    field_asts,  # type: List[Field]
-    info,  # type: ResolveInfo
-    path,  # type: List[Union[int, str]]
-    result,  # type: Any
-):
-    # type: (...) -> Any
-    """
-    Implements the instructions for completeValue as defined in the
-    "Field entries" section of the spec.
-
-    If the field type is Non-Null, then this recursively completes the value for the inner type. It throws a field
-    error if that completion returns null, as per the "Nullability" section of the spec.
-
-    If the field type is a List, then this recursively completes the value for the inner type on each item in the
-    list.
-
-    If the field type is a Scalar or Enum, ensures the completed value is a legal value of the type by calling the
-    `serialize` method of GraphQL type definition.
-
-    If the field is an abstract type, determine the runtime type of the value and then complete based on that type.
-
-    Otherwise, the field type expects a sub-selection set, and will complete the value by evaluating all
-    sub-selections.
-    """
-    # If field type is NonNull, complete for inner type, and throw field error
-    # if result is null.
-    if is_thenable(result):
-        return Promise.resolve(result).then(
-            lambda resolved: complete_value(
-                exe_context, return_type, field_asts, info, path, resolved
-            ),
-            lambda error: Promise.rejected(
-                GraphQLLocatedError(field_asts, original_error=error, path=path)
-            ),
-        )
-
-    # print return_type, type(result)
-    if isinstance(result, Exception):
-        raise GraphQLLocatedError(field_asts, original_error=result, path=path)
-
-    if isinstance(return_type, GraphQLNonNull):
-        return complete_nonnull_value(
-            exe_context, return_type, field_asts, info, path, result
-        )
-
-    # If result is null-like, return null.
-    if result is None:
-        return None
-
-    # If field type is List, complete each item in the list with the inner type
-    if isinstance(return_type, GraphQLList):
-        return complete_list_value(
-            exe_context, return_type, field_asts, info, path, result
-        )
-
-    # If field type is Scalar or Enum, serialize to a valid value, returning
-    # null if coercion is not possible.
-    if isinstance(return_type, (GraphQLScalarType, GraphQLEnumType)):
-        return complete_leaf_value(return_type, path, result)
-
-    if isinstance(return_type, (GraphQLInterfaceType, GraphQLUnionType)):
-        return complete_abstract_value(
-            exe_context, return_type, field_asts, info, path, result
-        )
-
-    if isinstance(return_type, GraphQLObjectType):
-        return complete_object_value(
-            exe_context, return_type, field_asts, info, path, result
-        )
-
-    assert False, u'Cannot complete value of unexpected type "{}".'.format(return_type)
-
-
-def complete_list_value(
-    exe_context,  # type: ExecutionContext
-    return_type,  # type: GraphQLList
-    field_asts,  # type: List[Field]
-    info,  # type: ResolveInfo
-    path,  # type: List[Union[int, str]]
-    result,  # type: Any
-):
-    # type: (...) -> List[Any]
-    """
-    Complete a list value by completing each item in the list with the inner type
-    """
-    assert isinstance(result, Iterable), (
-        "User Error: expected iterable, but did not find one " + "for field {}.{}."
-    ).format(info.parent_type, info.field_name)
-
-    item_type = return_type.of_type
-    completed_results = []
-    contains_promise = False
-
-    index = 0
-    for item in result:
-        completed_item = complete_value_catching_error(
-            exe_context, item_type, field_asts, info, path + [index], item
-        )
-        if not contains_promise and is_thenable(completed_item):
-            contains_promise = True
-
-        completed_results.append(completed_item)
-        index += 1
-
-    return Promise.all(completed_results) if contains_promise else completed_results
-
-
-def complete_leaf_value(
-    return_type,  # type: Union[GraphQLEnumType, GraphQLScalarType]
-    path,  # type: List[Union[int, str]]
-    result,  # type: Any
-):
-    # type: (...) -> Union[int, str, float, bool]
-    """
-    Complete a Scalar or Enum by serializing to a valid value, returning null if serialization is not possible.
-    """
-    assert hasattr(return_type, "serialize"), "Missing serialize method on type"
-    serialized_result = return_type.serialize(result)
-
-    if serialized_result is None:
-        raise GraphQLError(
-            ('Expected a value of type "{}" but ' + "received: {}").format(
-                return_type, result
-            ),
-            path=path,
-        )
-    return serialized_result
-
-
-def complete_abstract_value(
-    exe_context,  # type: ExecutionContext
-    return_type,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-    field_asts,  # type: List[Field]
-    info,  # type: ResolveInfo
-    path,  # type: List[Union[int, str]]
-    result,  # type: Any
-):
-    # type: (...) -> Dict[str, Any]
-    """
-    Complete an value of an abstract type by determining the runtime type of that value, then completing based
-    on that type.
-    """
-    runtime_type = None  # type: Union[str, GraphQLObjectType, None]
-
-    # Field type must be Object, Interface or Union and expect sub-selections.
-    if isinstance(return_type, (GraphQLInterfaceType, GraphQLUnionType)):
-        if return_type.resolve_type:
-            runtime_type = return_type.resolve_type(result, info)
-        else:
-            runtime_type = get_default_resolve_type_fn(result, info, return_type)
-
-    if isinstance(runtime_type, string_types):
-        runtime_type = info.schema.get_type(runtime_type)  # type: ignore
-
-    if not isinstance(runtime_type, GraphQLObjectType):
-        raise GraphQLError(
-            (
-                "Abstract type {} must resolve to an Object type at runtime "
-                + 'for field {}.{} with value "{}", received "{}".'
-            ).format(
-                return_type, info.parent_type, info.field_name, result, runtime_type
-            ),
-            field_asts,
-        )
-
-    if not exe_context.schema.is_possible_type(return_type, runtime_type):
-        raise GraphQLError(
-            u'Runtime Object type "{}" is not a possible type for "{}".'.format(
-                runtime_type, return_type
-            ),
-            field_asts,
-        )
-
-    return complete_object_value(
-        exe_context, runtime_type, field_asts, info, path, result
-    )
-
-
-def get_default_resolve_type_fn(
-    value,  # type: Any
-    info,  # type: ResolveInfo
-    abstract_type,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-):
-    # type: (...) -> Optional[GraphQLObjectType]
-    possible_types = info.schema.get_possible_types(abstract_type)
-    for type in possible_types:
-        if callable(type.is_type_of) and type.is_type_of(value, info):
-            return type
-    return None
-
-
-def complete_object_value(
-    exe_context,  # type: ExecutionContext
-    return_type,  # type: GraphQLObjectType
-    field_asts,  # type: List[Field]
-    info,  # type: ResolveInfo
-    path,  # type: List[Union[int, str]]
-    result,  # type: Any
-):
-    # type: (...) -> Dict[str, Any]
-    """
-    Complete an Object value by evaluating all sub-selections.
-    """
-    if return_type.is_type_of and not return_type.is_type_of(result, info):
-        raise GraphQLError(
-            u'Expected value of type "{}" but got: {}.'.format(
-                return_type, type(result).__name__
-            ),
-            field_asts,
-        )
-
-    # Collect sub-fields to execute to complete this value.
-    subfield_asts = exe_context.get_sub_fields(return_type, field_asts)
-    return execute_fields(exe_context, return_type, result, subfield_asts, path, info)
-
-
-def complete_nonnull_value(
-    exe_context,  # type: ExecutionContext
-    return_type,  # type: GraphQLNonNull
-    field_asts,  # type: List[Field]
-    info,  # type: ResolveInfo
-    path,  # type: List[Union[int, str]]
-    result,  # type: Any
-):
-    # type: (...) -> Any
-    """
-    Complete a NonNull value by completing the inner type
-    """
-    completed = complete_value(
-        exe_context, return_type.of_type, field_asts, info, path, result
-    )
-    if completed is None:
-        raise GraphQLError(
-            "Cannot return null for non-nullable field {}.{}.".format(
-                info.parent_type, info.field_name
-            ),
-            field_asts,
-            path=path,
-        )
-
-    return completed
diff --git a/graphql/execution/executors/__init__.py b/graphql/execution/executors/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/execution/executors/asyncio.py b/graphql/execution/executors/asyncio.py
deleted file mode 100644
index 7e01403..0000000
--- a/graphql/execution/executors/asyncio.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from __future__ import absolute_import
-
-from asyncio import Future, get_event_loop, iscoroutine, wait
-
-from promise import Promise
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from asyncio.unix_events import _UnixSelectorEventLoop
-    from typing import Optional, Any, Callable, List
-
-try:
-    from asyncio import ensure_future
-except ImportError:
-    # ensure_future is only implemented in Python 3.4.4+
-    def ensure_future(coro_or_future, loop=None):  # type: ignore
-        """Wrap a coroutine or an awaitable in a future.
-
-        If the argument is a Future, it is returned directly.
-        """
-        if isinstance(coro_or_future, Future):
-            if loop is not None and loop is not coro_or_future._loop:
-                raise ValueError("loop argument must agree with Future")
-            return coro_or_future
-        elif iscoroutine(coro_or_future):
-            if loop is None:
-                loop = get_event_loop()
-            task = loop.create_task(coro_or_future)
-            if task._source_traceback:
-                del task._source_traceback[-1]
-            return task
-        else:
-            raise TypeError("A Future, a coroutine or an awaitable is required")
-
-
-try:
-    from .asyncio_utils import asyncgen_to_observable, isasyncgen
-except Exception:
-
-    def isasyncgen(object):  # type: ignore
-        False
-
-    def asyncgen_to_observable(asyncgen, loop=None):
-        pass
-
-
-class AsyncioExecutor(object):
-    def __init__(self, loop=None):
-        # type: (Optional[_UnixSelectorEventLoop]) -> None
-        if loop is None:
-            loop = get_event_loop()
-        self.loop = loop
-        self.futures = []  # type: List[Future]
-
-    def wait_until_finished(self):
-        # type: () -> None
-        # if there are futures to wait for
-        while self.futures:
-            # wait for the futures to finish
-            futures = self.futures
-            self.futures = []
-            self.loop.run_until_complete(wait(futures))
-
-    def clean(self):
-        self.futures = []
-
-    def execute(self, fn, *args, **kwargs):
-        # type: (Callable, *Any, **Any) -> Any
-        result = fn(*args, **kwargs)
-        if isinstance(result, Future) or iscoroutine(result):
-            future = ensure_future(result, loop=self.loop)
-            self.futures.append(future)
-            return Promise.resolve(future)
-        elif isasyncgen(result):
-            return asyncgen_to_observable(result, loop=self.loop)
-        return result
diff --git a/graphql/execution/executors/asyncio_utils.py b/graphql/execution/executors/asyncio_utils.py
deleted file mode 100644
index 17ec5c7..0000000
--- a/graphql/execution/executors/asyncio_utils.py
+++ /dev/null
@@ -1,30 +0,0 @@
-from inspect import isasyncgen  # type: ignore
-from asyncio import ensure_future, wait, CancelledError
-from rx import AnonymousObservable
-
-
-def asyncgen_to_observable(asyncgen, loop=None):
-    def emit(observer):
-        task = ensure_future(iterate_asyncgen(asyncgen, observer), loop=loop)
-
-        def dispose():
-            async def await_task():
-                await task
-
-            task.cancel()
-            ensure_future(await_task(), loop=loop)
-
-        return dispose
-
-    return AnonymousObservable(emit)
-
-
-async def iterate_asyncgen(asyncgen, observer):
-    try:
-        async for item in asyncgen:
-            observer.on_next(item)
-        observer.on_completed()
-    except CancelledError:
-        pass
-    except Exception as e:
-        observer.on_error(e)
diff --git a/graphql/execution/executors/gevent.py b/graphql/execution/executors/gevent.py
deleted file mode 100644
index 4bc5ac4..0000000
--- a/graphql/execution/executors/gevent.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from __future__ import absolute_import
-
-import gevent
-from promise import Promise
-
-from .utils import process
-
-
-class GeventExecutor(object):
-    def __init__(self):
-        self.jobs = []
-
-    def wait_until_finished(self):
-        # gevent.joinall(self.jobs)
-        while self.jobs:
-            jobs = self.jobs
-            self.jobs = []
-            [j.join() for j in jobs]
-
-    def clean(self):
-        self.jobs = []
-
-    def execute(self, fn, *args, **kwargs):
-        promise = Promise()
-        job = gevent.spawn(process, promise, fn, args, kwargs)
-        self.jobs.append(job)
-        return promise
diff --git a/graphql/execution/executors/process.py b/graphql/execution/executors/process.py
deleted file mode 100644
index 948279a..0000000
--- a/graphql/execution/executors/process.py
+++ /dev/null
@@ -1,36 +0,0 @@
-from multiprocessing import Process, Queue
-
-from promise import Promise
-
-from .utils import process
-
-
-def queue_process(q):
-    promise, fn, args, kwargs = q.get()
-    process(promise, fn, args, kwargs)
-
-
-class ProcessExecutor(object):
-    def __init__(self):
-        self.processes = []
-        self.q = Queue()
-
-    def wait_until_finished(self):
-        while self.processes:
-            processes = self.processes
-            self.processes = []
-            [_process.join() for _process in processes]
-        self.q.close()
-        self.q.join_thread()
-
-    def clean(self):
-        self.processes = []
-
-    def execute(self, fn, *args, **kwargs):
-        promise = Promise()
-
-        self.q.put([promise, fn, args, kwargs], False)
-        _process = Process(target=queue_process, args=(self.q))
-        _process.start()
-        self.processes.append(_process)
-        return promise
diff --git a/graphql/execution/executors/sync.py b/graphql/execution/executors/sync.py
deleted file mode 100644
index c45d8a8..0000000
--- a/graphql/execution/executors/sync.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, Callable
-
-
-class SyncExecutor(object):
-    def wait_until_finished(self):
-        # type: () -> None
-        pass
-
-    def clean(self):
-        pass
-
-    def execute(self, fn, *args, **kwargs):
-        # type: (Callable, *Any, **Any) -> Any
-        return fn(*args, **kwargs)
diff --git a/graphql/execution/executors/thread.py b/graphql/execution/executors/thread.py
deleted file mode 100644
index f540a1a..0000000
--- a/graphql/execution/executors/thread.py
+++ /dev/null
@@ -1,47 +0,0 @@
-from multiprocessing.pool import ThreadPool
-from threading import Thread
-
-from promise import Promise
-from .utils import process
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, Callable, List
-
-
-class ThreadExecutor(object):
-
-    pool = None
-
-    def __init__(self, pool=False):
-        # type: (bool) -> None
-        self.threads = []  # type: List[Thread]
-        if pool:
-            self.execute = self.execute_in_pool
-            self.pool = ThreadPool(processes=pool)
-        else:
-            self.execute = self.execute_in_thread
-
-    def wait_until_finished(self):
-        # type: () -> None
-        while self.threads:
-            threads = self.threads
-            self.threads = []
-            for thread in threads:
-                thread.join()
-
-    def clean(self):
-        self.threads = []
-
-    def execute_in_thread(self, fn, *args, **kwargs):
-        # type: (Callable, *Any, **Any) -> Promise
-        promise = Promise()
-        thread = Thread(target=process, args=(promise, fn, args, kwargs))
-        thread.start()
-        self.threads.append(thread)
-        return promise
-
-    def execute_in_pool(self, fn, *args, **kwargs):
-        promise = Promise()
-        self.pool.map(lambda input: process(*input), [(promise, fn, args, kwargs)])
-        return promise
diff --git a/graphql/execution/executors/utils.py b/graphql/execution/executors/utils.py
deleted file mode 100644
index 869a81c..0000000
--- a/graphql/execution/executors/utils.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from sys import exc_info
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..base import ResolveInfo
-    from promise import Promise
-    from typing import Callable, Dict, Tuple, Any
-
-
-def process(
-    p,  # type: Promise
-    f,  # type: Callable
-    args,  # type: Tuple[Any, ResolveInfo]
-    kwargs,  # type: Dict[str, Any]
-):
-    # type: (...) -> None
-    try:
-        val = f(*args, **kwargs)
-        p.do_resolve(val)
-    except Exception as e:
-        traceback = exc_info()[2]
-        e.stack = traceback  # type: ignore
-        p.do_reject(e, traceback=traceback)
diff --git a/graphql/execution/middleware.py b/graphql/execution/middleware.py
deleted file mode 100644
index dd00fd4..0000000
--- a/graphql/execution/middleware.py
+++ /dev/null
@@ -1,75 +0,0 @@
-import inspect
-from functools import partial
-from itertools import chain
-
-from promise import promisify
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, Callable, Iterator, Tuple, Dict, Iterable
-
-MIDDLEWARE_RESOLVER_FUNCTION = "resolve"
-
-
-class MiddlewareManager(object):
-    __slots__ = (
-        "middlewares",
-        "wrap_in_promise",
-        "_middleware_resolvers",
-        "_cached_resolvers",
-    )
-
-    def __init__(self, *middlewares, **kwargs):
-        # type: (*Callable, **bool) -> None
-        self.middlewares = middlewares
-        self.wrap_in_promise = kwargs.get("wrap_in_promise", True)
-        self._middleware_resolvers = (
-            list(get_middleware_resolvers(middlewares)) if middlewares else []
-        )
-        self._cached_resolvers = {}  # type: Dict[Callable, Callable]
-
-    def get_field_resolver(self, field_resolver):
-        # type: (Callable) -> Callable
-        if field_resolver not in self._cached_resolvers:
-            self._cached_resolvers[field_resolver] = middleware_chain(
-                field_resolver,
-                self._middleware_resolvers,
-                wrap_in_promise=self.wrap_in_promise,
-            )
-
-        return self._cached_resolvers[field_resolver]
-
-
-middlewares = MiddlewareManager
-
-
-def get_middleware_resolvers(middlewares):
-    # type: (Tuple[Any, ...]) -> Iterator[Callable]
-    for middleware in middlewares:
-        # If the middleware is a function instead of a class
-        if inspect.isfunction(middleware):
-            yield middleware
-        if not hasattr(middleware, MIDDLEWARE_RESOLVER_FUNCTION):
-            continue
-        yield getattr(middleware, MIDDLEWARE_RESOLVER_FUNCTION)
-
-
-def middleware_chain(func, middlewares, wrap_in_promise):
-    # type: (Callable, Iterable[Callable], bool) -> Callable
-    if not middlewares:
-        return func
-    if wrap_in_promise:
-        middlewares = chain((func, make_it_promise), middlewares)
-    else:
-        middlewares = chain((func,), middlewares)
-    last_func = None
-    for middleware in middlewares:
-        last_func = partial(middleware, last_func) if last_func else middleware
-
-    return last_func  # type: ignore
-
-
-@promisify
-def make_it_promise(next, *args, **kwargs):
-    # type: (Callable, *Any, **Any) -> Any
-    return next(*args, **kwargs)
diff --git a/graphql/execution/tests/__init__.py b/graphql/execution/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/execution/tests/test_abstract.py b/graphql/execution/tests/test_abstract.py
deleted file mode 100644
index ef3eacd..0000000
--- a/graphql/execution/tests/test_abstract.py
+++ /dev/null
@@ -1,390 +0,0 @@
-# type: ignore
-from graphql import graphql
-from graphql.type import GraphQLBoolean, GraphQLSchema, GraphQLString
-from graphql.type.definition import (
-    GraphQLField,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLObjectType,
-    GraphQLUnionType,
-)
-
-
-class Dog(object):
-    def __init__(self, name, woofs):
-        # type: (str, bool) -> None
-        self.name = name
-        self.woofs = woofs
-
-
-class Cat(object):
-    def __init__(self, name, meows):
-        # type: (str, bool) -> None
-        self.name = name
-        self.meows = meows
-
-
-class Human(object):
-    def __init__(self, name):
-        # type: (str) -> None
-        self.name = name
-
-
-def is_type_of(type):
-    # type: (type) -> Callable
-    return lambda obj, info: isinstance(obj, type)
-
-
-def make_type_resolver(types):
-    # type: (Callable) -> Callable
-    def resolve_type(obj, info):
-        # type: (Union[Cat, Dog, Human], ResolveInfo) -> GraphQLObjectType
-        if callable(types):
-            t = types()
-        else:
-            t = types
-
-        for klass, type in t:
-            if isinstance(obj, klass):
-                return type
-
-        return None
-
-    return resolve_type
-
-
-def test_is_type_of_used_to_resolve_runtime_type_for_interface():
-    # type: () -> None
-    PetType = GraphQLInterfaceType(
-        name="Pet", fields={"name": GraphQLField(GraphQLString)}
-    )
-
-    DogType = GraphQLObjectType(
-        name="Dog",
-        interfaces=[PetType],
-        is_type_of=is_type_of(Dog),
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "woofs": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    CatType = GraphQLObjectType(
-        name="Cat",
-        interfaces=[PetType],
-        is_type_of=is_type_of(Cat),
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "meows": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "pets": GraphQLField(
-                    GraphQLList(PetType),
-                    resolver=lambda *_: [Dog("Odie", True), Cat("Garfield", False)],
-                )
-            },
-        ),
-        types=[CatType, DogType],
-    )
-
-    query = """
-    {
-        pets {
-            name
-            ... on Dog {
-                woofs
-            }
-            ... on Cat {
-                meows
-            }
-        }
-    }
-    """
-
-    result = graphql(schema, query)
-    assert not result.errors
-    assert result.data == {
-        "pets": [{"woofs": True, "name": "Odie"}, {"name": "Garfield", "meows": False}]
-    }
-
-
-def test_is_type_of_used_to_resolve_runtime_type_for_union():
-    # type: () -> None
-    DogType = GraphQLObjectType(
-        name="Dog",
-        is_type_of=is_type_of(Dog),
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "woofs": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    CatType = GraphQLObjectType(
-        name="Cat",
-        is_type_of=is_type_of(Cat),
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "meows": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    PetType = GraphQLUnionType(name="Pet", types=[CatType, DogType])
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "pets": GraphQLField(
-                    GraphQLList(PetType),
-                    resolver=lambda *_: [Dog("Odie", True), Cat("Garfield", False)],
-                )
-            },
-        ),
-        types=[CatType, DogType],
-    )
-
-    query = """
-    {
-        pets {
-            ... on Dog {
-                name
-                woofs
-            }
-            ... on Cat {
-                name
-                meows
-            }
-        }
-    }
-    """
-
-    result = graphql(schema, query)
-    assert not result.errors
-    assert result.data == {
-        "pets": [{"woofs": True, "name": "Odie"}, {"name": "Garfield", "meows": False}]
-    }
-
-
-def test_resolve_type_on_interface_yields_useful_error():
-    # type: () -> None
-    PetType = GraphQLInterfaceType(
-        name="Pet",
-        fields={"name": GraphQLField(GraphQLString)},
-        resolve_type=make_type_resolver(
-            lambda: [(Dog, DogType), (Cat, CatType), (Human, HumanType)]
-        ),
-    )
-
-    DogType = GraphQLObjectType(
-        name="Dog",
-        interfaces=[PetType],
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "woofs": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    HumanType = GraphQLObjectType(
-        name="Human", fields={"name": GraphQLField(GraphQLString)}
-    )
-
-    CatType = GraphQLObjectType(
-        name="Cat",
-        interfaces=[PetType],
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "meows": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "pets": GraphQLField(
-                    GraphQLList(PetType),
-                    resolver=lambda *_: [
-                        Dog("Odie", True),
-                        Cat("Garfield", False),
-                        Human("Jon"),
-                    ],
-                )
-            },
-        ),
-        types=[DogType, CatType],
-    )
-
-    query = """
-    {
-        pets {
-            name
-            ... on Dog {
-                woofs
-            }
-            ... on Cat {
-                meows
-            }
-        }
-    }
-    """
-
-    result = graphql(schema, query)
-    assert (
-        result.errors[0].message
-        == 'Runtime Object type "Human" is not a possible type for "Pet".'
-    )
-    assert result.data == {
-        "pets": [
-            {"woofs": True, "name": "Odie"},
-            {"name": "Garfield", "meows": False},
-            None,
-        ]
-    }
-
-
-def test_resolve_type_on_union_yields_useful_error():
-    # type: () -> None
-    DogType = GraphQLObjectType(
-        name="Dog",
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "woofs": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    HumanType = GraphQLObjectType(
-        name="Human", fields={"name": GraphQLField(GraphQLString)}
-    )
-
-    CatType = GraphQLObjectType(
-        name="Cat",
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "meows": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    PetType = GraphQLUnionType(
-        name="Pet",
-        types=[DogType, CatType],
-        resolve_type=make_type_resolver(
-            lambda: [(Dog, DogType), (Cat, CatType), (Human, HumanType)]
-        ),
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "pets": GraphQLField(
-                    GraphQLList(PetType),
-                    resolver=lambda *_: [
-                        Dog("Odie", True),
-                        Cat("Garfield", False),
-                        Human("Jon"),
-                    ],
-                )
-            },
-        )
-    )
-
-    query = """
-    {
-        pets {
-            ... on Dog {
-                name
-                woofs
-            }
-            ... on Cat {
-                name
-                meows
-            }
-        }
-    }
-    """
-
-    result = graphql(schema, query)
-    assert (
-        result.errors[0].message
-        == 'Runtime Object type "Human" is not a possible type for "Pet".'
-    )
-    assert result.data == {
-        "pets": [
-            {"woofs": True, "name": "Odie"},
-            {"name": "Garfield", "meows": False},
-            None,
-        ]
-    }
-
-
-def test_resolve_type_can_use_type_string():
-    # type: () -> None
-
-    def type_string_resolver(obj, *_):
-        # type: (Union[Cat, Dog], *ResolveInfo) -> str
-        if isinstance(obj, Dog):
-            return "Dog"
-        if isinstance(obj, Cat):
-            return "Cat"
-
-    PetType = GraphQLInterfaceType(
-        name="Pet",
-        fields={"name": GraphQLField(GraphQLString)},
-        resolve_type=type_string_resolver,
-    )
-
-    DogType = GraphQLObjectType(
-        name="Dog",
-        interfaces=[PetType],
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "woofs": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    CatType = GraphQLObjectType(
-        name="Cat",
-        interfaces=[PetType],
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "meows": GraphQLField(GraphQLBoolean),
-        },
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "pets": GraphQLField(
-                    GraphQLList(PetType),
-                    resolver=lambda *_: [Dog("Odie", True), Cat("Garfield", False)],
-                )
-            },
-        ),
-        types=[CatType, DogType],
-    )
-
-    query = """
-    {
-        pets {
-            name
-            ... on Dog {
-                woofs
-            }
-            ... on Cat {
-                meows
-            }
-        }
-    }
-    """
-
-    result = graphql(schema, query)
-    assert not result.errors
-    assert result.data == {
-        "pets": [{"woofs": True, "name": "Odie"}, {"name": "Garfield", "meows": False}]
-    }
diff --git a/graphql/execution/tests/test_benchmark.py b/graphql/execution/tests/test_benchmark.py
deleted file mode 100644
index 04b5108..0000000
--- a/graphql/execution/tests/test_benchmark.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# type: ignore
-from collections import namedtuple
-from functools import partial
-
-from graphql import (
-    GraphQLField,
-    GraphQLInt,
-    GraphQLList,
-    GraphQLObjectType,
-    GraphQLSchema,
-    Source,
-    execute,
-    parse,
-)
-
-# from graphql.execution import executor
-
-# executor.use_experimental_executor = True
-
-SIZE = 10000
-# set global fixtures
-Container = namedtuple("Container", "x y z o")
-big_int_list = [x for x in range(SIZE)]
-big_container_list = [Container(x=x, y=x, z=x, o=x) for x in range(SIZE)]
-
-ContainerType = GraphQLObjectType(
-    "Container",
-    fields={
-        "x": GraphQLField(GraphQLInt),
-        "y": GraphQLField(GraphQLInt),
-        "z": GraphQLField(GraphQLInt),
-        "o": GraphQLField(GraphQLInt),
-    },
-)
-
-
-def resolve_all_containers(root, info, **args):
-    return big_container_list
-
-
-def resolve_all_ints(root, info, **args):
-    return big_int_list
-
-
-def test_big_list_of_ints(benchmark):
-    Query = GraphQLObjectType(
-        "Query",
-        fields={
-            "allInts": GraphQLField(GraphQLList(GraphQLInt), resolver=resolve_all_ints)
-        },
-    )
-    schema = GraphQLSchema(Query)
-    source = Source("{ allInts }")
-    ast = parse(source)
-
-    @benchmark
-    def b():
-        return execute(schema, ast)
-
-
-def test_big_list_of_ints_serialize(benchmark):
-    from ..executor import complete_leaf_value
-
-    @benchmark
-    def serialize():
-        map(GraphQLInt.serialize, big_int_list)
-
-
-def test_big_list_objecttypes_with_one_int_field(benchmark):
-    Query = GraphQLObjectType(
-        "Query",
-        fields={
-            "allContainers": GraphQLField(
-                GraphQLList(ContainerType), resolver=resolve_all_containers
-            )
-        },
-    )
-    schema = GraphQLSchema(Query)
-    source = Source("{ allContainers { x } }")
-    ast = parse(source)
-
-    @benchmark
-    def b():
-        return execute(schema, ast)
-
-
-def test_big_list_objecttypes_with_two_int_fields(benchmark):
-    Query = GraphQLObjectType(
-        "Query",
-        fields={
-            "allContainers": GraphQLField(
-                GraphQLList(ContainerType), resolver=resolve_all_containers
-            )
-        },
-    )
-
-    schema = GraphQLSchema(Query)
-    source = Source("{ allContainers { x, y } }")
-    ast = parse(source)
-
-    @benchmark
-    def b():
-        return execute(schema, ast)
diff --git a/graphql/execution/tests/test_dataloader.py b/graphql/execution/tests/test_dataloader.py
deleted file mode 100644
index 88f292d..0000000
--- a/graphql/execution/tests/test_dataloader.py
+++ /dev/null
@@ -1,171 +0,0 @@
-# type: ignore
-import pytest
-from promise import Promise
-from promise.dataloader import DataLoader
-
-from graphql import (
-    GraphQLObjectType,
-    GraphQLField,
-    GraphQLID,
-    GraphQLArgument,
-    GraphQLNonNull,
-    GraphQLSchema,
-    parse,
-    execute,
-)
-from graphql.execution.executors.sync import SyncExecutor
-from graphql.execution.executors.thread import ThreadExecutor
-
-
-@pytest.mark.parametrize(
-    "executor",
-    [
-        SyncExecutor(),
-        # ThreadExecutor(),
-    ],
-)
-def test_batches_correctly(executor):
-    # type: (SyncExecutor) -> None
-
-    Business = GraphQLObjectType(
-        "Business",
-        lambda: {
-            "id": GraphQLField(GraphQLID, resolver=lambda root, info, **args: root)
-        },
-    )
-
-    Query = GraphQLObjectType(
-        "Query",
-        lambda: {
-            "getBusiness": GraphQLField(
-                Business,
-                args={"id": GraphQLArgument(GraphQLNonNull(GraphQLID))},
-                resolver=lambda root, info, **args: info.context.business_data_loader.load(
-                    args.get("id")
-                ),
-            )
-        },
-    )
-
-    schema = GraphQLSchema(query=Query)
-
-    doc = """
-{
-    business1: getBusiness(id: "1") {
-        id
-    }
-    business2: getBusiness(id: "2") {
-        id
-    }
-}
-    """
-    doc_ast = parse(doc)
-
-    load_calls = []
-
-    class BusinessDataLoader(DataLoader):
-        def batch_load_fn(self, keys):
-            # type: (List[str]) -> Promise
-            load_calls.append(keys)
-            return Promise.resolve(keys)
-
-    class Context(object):
-        business_data_loader = BusinessDataLoader()
-
-    result = execute(schema, doc_ast, None, context_value=Context(), executor=executor)
-    assert not result.errors
-    assert result.data == {"business1": {"id": "1"}, "business2": {"id": "2"}}
-    assert load_calls == [["1", "2"]]
-
-
-@pytest.mark.parametrize(
-    "executor",
-    [
-        SyncExecutor(),
-        # ThreadExecutor(),  # Fails on pypy :O
-    ],
-)
-def test_batches_multiple_together(executor):
-    # type: (SyncExecutor) -> None
-
-    Location = GraphQLObjectType(
-        "Location",
-        lambda: {
-            "id": GraphQLField(GraphQLID, resolver=lambda root, info, **args: root)
-        },
-    )
-
-    Business = GraphQLObjectType(
-        "Business",
-        lambda: {
-            "id": GraphQLField(GraphQLID, resolver=lambda root, info, **args: root),
-            "location": GraphQLField(
-                Location,
-                resolver=lambda root, info, **args: info.context.location_data_loader.load(
-                    "location-{}".format(root)
-                ),
-            ),
-        },
-    )
-
-    Query = GraphQLObjectType(
-        "Query",
-        lambda: {
-            "getBusiness": GraphQLField(
-                Business,
-                args={"id": GraphQLArgument(GraphQLNonNull(GraphQLID))},
-                resolver=lambda root, info, **args: info.context.business_data_loader.load(
-                    args.get("id")
-                ),
-            )
-        },
-    )
-
-    schema = GraphQLSchema(query=Query)
-
-    doc = """
-{
-    business1: getBusiness(id: "1") {
-        id
-        location {
-            id
-        }
-    }
-    business2: getBusiness(id: "2") {
-        id
-        location {
-            id
-        }
-    }
-}
-    """
-    doc_ast = parse(doc)
-
-    business_load_calls = []
-
-    class BusinessDataLoader(DataLoader):
-        def batch_load_fn(self, keys):
-            # type: (List[str]) -> Promise
-            business_load_calls.append(keys)
-            return Promise.resolve(keys)
-
-    location_load_calls = []
-
-    class LocationDataLoader(DataLoader):
-        def batch_load_fn(self, keys):
-            # type: (List[str]) -> Promise
-            location_load_calls.append(keys)
-            return Promise.resolve(keys)
-
-    class Context(object):
-        business_data_loader = BusinessDataLoader()
-        location_data_loader = LocationDataLoader()
-
-    result = execute(schema, doc_ast, None, context_value=Context(), executor=executor)
-    assert not result.errors
-    assert result.data == {
-        "business1": {"id": "1", "location": {"id": "location-1"}},
-        "business2": {"id": "2", "location": {"id": "location-2"}},
-    }
-    assert business_load_calls == [["1", "2"]]
-    assert location_load_calls == [["location-1", "location-2"]]
diff --git a/graphql/execution/tests/test_directives.py b/graphql/execution/tests/test_directives.py
deleted file mode 100644
index acc1ac1..0000000
--- a/graphql/execution/tests/test_directives.py
+++ /dev/null
@@ -1,277 +0,0 @@
-# type: ignore
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
-
-schema = GraphQLSchema(
-    query=GraphQLObjectType(
-        name="TestType",
-        fields={"a": GraphQLField(GraphQLString), "b": GraphQLField(GraphQLString)},
-    )
-)
-
-
-class Data(object):
-    a = "a"
-    b = "b"
-
-
-def execute_test_query(doc):
-    # type: (str) -> ExecutionResult
-    return execute(schema, parse(doc), Data)
-
-
-def test_basic_query_works():
-    # type: () -> None
-    result = execute_test_query("{ a, b }")
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_if_true_includes_scalar():
-    # type: () -> None
-    result = execute_test_query("{ a, b @include(if: true) }")
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_if_false_omits_on_scalar():
-    # type: () -> None
-    result = execute_test_query("{ a, b @include(if: false) }")
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_skip_false_includes_scalar():
-    # type: () -> None
-    result = execute_test_query("{ a, b @skip(if: false) }")
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_skip_true_omits_scalar():
-    # type: () -> None
-    result = execute_test_query("{ a, b @skip(if: true) }")
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_if_false_omits_fragment_spread():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ...Frag @include(if: false)
-        }
-        fragment Frag on TestType {
-          b
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_if_true_includes_fragment_spread():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ...Frag @include(if: true)
-        }
-        fragment Frag on TestType {
-          b
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_skip_false_includes_fragment_spread():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ...Frag @skip(if: false)
-        }
-        fragment Frag on TestType {
-          b
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_skip_true_omits_fragment_spread():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ...Frag @skip(if: true)
-        }
-        fragment Frag on TestType {
-          b
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_if_false_omits_inline_fragment():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ... on TestType @include(if: false) {
-            b
-          }
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_if_true_includes_inline_fragment():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ... on TestType @include(if: true) {
-            b
-          }
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_skip_false_includes_inline_fragment():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ... on TestType @skip(if: false) {
-            b
-          }
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_skip_true_omits_inline_fragment():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ... on TestType @skip(if: true) {
-            b
-          }
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_skip_true_omits_fragment():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ...Frag
-        }
-        fragment Frag on TestType @skip(if: true) {
-          b
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_skip_on_inline_anonymous_fragment_omits_field():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ... @skip(if: true) {
-            b
-          }
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_skip_on_inline_anonymous_fragment_does_not_omit_field():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ... @skip(if: false) {
-            b
-          }
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_include_on_inline_anonymous_fragment_omits_field():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ... @include(if: false) {
-            b
-          }
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_include_on_inline_anonymous_fragment_does_not_omit_field():
-    # type: () -> None
-    q = """
-        query Q {
-          a
-          ... @include(if: true) {
-            b
-          }
-        }
-    """
-    result = execute_test_query(q)
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_works_directives_include_and_no_skip():
-    # type: () -> None
-    result = execute_test_query("{ a, b @include(if: true) @skip(if: false) }")
-    assert not result.errors
-    assert result.data == {"a": "a", "b": "b"}
-
-
-def test_works_directives_include_and_skip():
-    # type: () -> None
-    result = execute_test_query("{ a, b @include(if: true) @skip(if: true) }")
-    assert not result.errors
-    assert result.data == {"a": "a"}
-
-
-def test_works_directives_no_include_or_skip():
-    # type: () -> None
-    result = execute_test_query("{ a, b @include(if: false) @skip(if: false) }")
-    assert not result.errors
-    assert result.data == {"a": "a"}
diff --git a/graphql/execution/tests/test_execute_schema.py b/graphql/execution/tests/test_execute_schema.py
deleted file mode 100644
index 0c7e103..0000000
--- a/graphql/execution/tests/test_execute_schema.py
+++ /dev/null
@@ -1,177 +0,0 @@
-# type: ignore
-
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLBoolean,
-    GraphQLField,
-    GraphQLID,
-    GraphQLInt,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-
-
-def test_executes_using_a_schema():
-    # type: () -> None
-    BlogImage = GraphQLObjectType(
-        "BlogImage",
-        {
-            "url": GraphQLField(GraphQLString),
-            "width": GraphQLField(GraphQLInt),
-            "height": GraphQLField(GraphQLInt),
-        },
-    )
-
-    BlogAuthor = GraphQLObjectType(
-        "Author",
-        lambda: {
-            "id": GraphQLField(GraphQLString),
-            "name": GraphQLField(GraphQLString),
-            "pic": GraphQLField(
-                BlogImage,
-                args={
-                    "width": GraphQLArgument(GraphQLInt),
-                    "height": GraphQLArgument(GraphQLInt),
-                },
-                resolver=lambda obj, info, **args: obj.pic(
-                    args["width"], args["height"]
-                ),
-            ),
-            "recentArticle": GraphQLField(BlogArticle),
-        },
-    )
-
-    BlogArticle = GraphQLObjectType(
-        "Article",
-        {
-            "id": GraphQLField(GraphQLNonNull(GraphQLString)),
-            "isPublished": GraphQLField(GraphQLBoolean),
-            "author": GraphQLField(BlogAuthor),
-            "title": GraphQLField(GraphQLString),
-            "body": GraphQLField(GraphQLString),
-            "keywords": GraphQLField(GraphQLList(GraphQLString)),
-        },
-    )
-
-    BlogQuery = GraphQLObjectType(
-        "Query",
-        {
-            "article": GraphQLField(
-                BlogArticle,
-                args={"id": GraphQLArgument(GraphQLID)},
-                resolver=lambda obj, info, **args: Article(args["id"]),
-            ),
-            "feed": GraphQLField(
-                GraphQLList(BlogArticle),
-                resolver=lambda *_: map(Article, range(1, 10 + 1)),
-            ),
-        },
-    )
-
-    BlogSchema = GraphQLSchema(BlogQuery)
-
-    class Article(object):
-        def __init__(self, id):
-            # type: (int) -> None
-            self.id = id
-            self.isPublished = True
-            self.author = Author()
-            self.title = "My Article {}".format(id)
-            self.body = "This is a post"
-            self.hidden = "This data is not exposed in the schema"
-            self.keywords = ["foo", "bar", 1, True, None]
-
-    class Author(object):
-        id = 123
-        name = "John Smith"
-
-        def pic(self, width, height):
-            # type: (int, int) -> Pic
-            return Pic(123, width, height)
-
-        @property
-        def recentArticle(self):
-            # type: () -> Article
-            return Article(1)
-
-    class Pic(object):
-        def __init__(self, uid, width, height):
-            # type: (int, int, int) -> None
-            self.url = "cdn://{}".format(uid)
-            self.width = str(width)
-            self.height = str(height)
-
-    request = """
-    {
-        feed {
-          id,
-          title
-        },
-        article(id: "1") {
-          ...articleFields,
-          author {
-            id,
-            name,
-            pic(width: 640, height: 480) {
-              url,
-              width,
-              height
-            },
-            recentArticle {
-              ...articleFields,
-              keywords
-            }
-          }
-        }
-      }
-      fragment articleFields on Article {
-        id,
-        isPublished,
-        title,
-        body,
-        hidden,
-        notdefined
-      }
-    """
-
-    # Note: this is intentionally not validating to ensure appropriate
-    # behavior occurs when executing an invalid query.
-    result = execute(BlogSchema, parse(request))
-    assert not result.errors
-    assert result.data == {
-        "feed": [
-            {"id": "1", "title": "My Article 1"},
-            {"id": "2", "title": "My Article 2"},
-            {"id": "3", "title": "My Article 3"},
-            {"id": "4", "title": "My Article 4"},
-            {"id": "5", "title": "My Article 5"},
-            {"id": "6", "title": "My Article 6"},
-            {"id": "7", "title": "My Article 7"},
-            {"id": "8", "title": "My Article 8"},
-            {"id": "9", "title": "My Article 9"},
-            {"id": "10", "title": "My Article 10"},
-        ],
-        "article": {
-            "id": "1",
-            "isPublished": True,
-            "title": "My Article 1",
-            "body": "This is a post",
-            "author": {
-                "id": "123",
-                "name": "John Smith",
-                "pic": {"url": "cdn://123", "width": 640, "height": 480},
-                "recentArticle": {
-                    "id": "1",
-                    "isPublished": True,
-                    "title": "My Article 1",
-                    "body": "This is a post",
-                    "keywords": ["foo", "bar", "1", "true", None],
-                },
-            },
-        },
-    }
diff --git a/graphql/execution/tests/test_executor.py b/graphql/execution/tests/test_executor.py
deleted file mode 100644
index b4298e8..0000000
--- a/graphql/execution/tests/test_executor.py
+++ /dev/null
@@ -1,761 +0,0 @@
-# type: ignore
-import json
-
-from pytest import raises
-
-from graphql.error import GraphQLError
-from graphql.execution import MiddlewareManager, execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLBoolean,
-    GraphQLField,
-    GraphQLInt,
-    GraphQLList,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLNonNull,
-    GraphQLID,
-)
-from promise import Promise
-
-
-def test_executes_arbitary_code():
-    # type: () -> None
-    class Data(object):
-        a = "Apple"
-        b = "Banana"
-        c = "Cookie"
-        d = "Donut"
-        e = "Egg"
-        f = "Fish"
-
-        def pic(self, size=50):
-            # type: (int) -> str
-            return "Pic of size: {}".format(size)
-
-        def deep(self):
-            # type: () -> DeepData
-            return DeepData()
-
-        def promise(self):
-            # type: () -> Data
-            # FIXME: promise is unsupported
-            return Data()
-
-    class DeepData(object):
-        a = "Already Been Done"
-        b = "Boring"
-        c = ["Contrived", None, "Confusing"]
-
-        def deeper(self):
-            # type: () -> List[Optional[Data]]
-            return [Data(), None, Data()]
-
-    doc = """
-        query Example($size: Int) {
-            a,
-            b,
-            x: c
-            ...c
-            f
-            ...on DataType {
-                pic(size: $size)
-                promise {
-                    a
-                }
-            }
-            deep {
-                a
-                b
-                c
-                deeper {
-                    a
-                    b
-                }
-            }
-        }
-        fragment c on DataType {
-            d
-            e
-        }
-    """
-
-    ast = parse(doc)
-    expected = {
-        "a": "Apple",
-        "b": "Banana",
-        "x": "Cookie",
-        "d": "Donut",
-        "e": "Egg",
-        "f": "Fish",
-        "pic": "Pic of size: 100",
-        "promise": {"a": "Apple"},
-        "deep": {
-            "a": "Already Been Done",
-            "b": "Boring",
-            "c": ["Contrived", None, "Confusing"],
-            "deeper": [
-                {"a": "Apple", "b": "Banana"},
-                None,
-                {"a": "Apple", "b": "Banana"},
-            ],
-        },
-    }
-
-    DataType = GraphQLObjectType(
-        "DataType",
-        lambda: {
-            "a": GraphQLField(GraphQLString),
-            "b": GraphQLField(GraphQLString),
-            "c": GraphQLField(GraphQLString),
-            "d": GraphQLField(GraphQLString),
-            "e": GraphQLField(GraphQLString),
-            "f": GraphQLField(GraphQLString),
-            "pic": GraphQLField(
-                args={"size": GraphQLArgument(GraphQLInt)},
-                type=GraphQLString,
-                resolver=lambda obj, info, size: obj.pic(size),
-            ),
-            "deep": GraphQLField(DeepDataType),
-            "promise": GraphQLField(DataType),
-        },
-    )
-
-    DeepDataType = GraphQLObjectType(
-        "DeepDataType",
-        {
-            "a": GraphQLField(GraphQLString),
-            "b": GraphQLField(GraphQLString),
-            "c": GraphQLField(GraphQLList(GraphQLString)),
-            "deeper": GraphQLField(GraphQLList(DataType)),
-        },
-    )
-
-    schema = GraphQLSchema(query=DataType)
-
-    result = execute(
-        schema, ast, Data(), operation_name="Example", variable_values={"size": 100}
-    )
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_merges_parallel_fragments():
-    # type: () -> None
-    ast = parse(
-        """
-        { a, deep {...FragOne, ...FragTwo} }
-
-        fragment FragOne on Type {
-            b
-            deep { b, deeper: deep { b } }
-        }
-
-        fragment FragTwo on Type {
-            c
-            deep { c, deeper: deep { c } }
-        }
-    """
-    )
-
-    Type = GraphQLObjectType(
-        "Type",
-        lambda: {
-            "a": GraphQLField(GraphQLString, resolver=lambda *_: "Apple"),
-            "b": GraphQLField(GraphQLString, resolver=lambda *_: "Banana"),
-            "c": GraphQLField(GraphQLString, resolver=lambda *_: "Cherry"),
-            "deep": GraphQLField(Type, resolver=lambda *_: {}),
-        },
-    )
-
-    schema = GraphQLSchema(query=Type)
-    result = execute(schema, ast)
-    assert not result.errors
-    assert result.data == {
-        "a": "Apple",
-        "deep": {
-            "b": "Banana",
-            "c": "Cherry",
-            "deep": {
-                "b": "Banana",
-                "c": "Cherry",
-                "deeper": {"b": "Banana", "c": "Cherry"},
-            },
-        },
-    }
-
-
-def test_threads_root_value_context_correctly():
-    # type: () -> None
-    doc = "query Example { a }"
-
-    class Data(object):
-        context_thing = "thing"
-
-    ast = parse(doc)
-
-    def resolver(root_value, *_):
-        # type: (Data, *ResolveInfo) -> None
-        assert root_value.context_thing == "thing"
-        resolver.got_here = True
-
-    resolver.got_here = False
-
-    Type = GraphQLObjectType(
-        "Type", {"a": GraphQLField(GraphQLString, resolver=resolver)}
-    )
-
-    result = execute(GraphQLSchema(Type), ast, Data(), operation_name="Example")
-    assert not result.errors
-    assert resolver.got_here
-
-
-def test_correctly_threads_arguments():
-    # type: () -> None
-    doc = """
-        query Example {
-            b(numArg: 123, stringArg: "foo")
-        }
-    """
-
-    def resolver(source, info, numArg, stringArg):
-        # type: (Optional[Any], ResolveInfo, int, str) -> None
-        assert numArg == 123
-        assert stringArg == "foo"
-        resolver.got_here = True
-
-    resolver.got_here = False
-
-    doc_ast = parse(doc)
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "b": GraphQLField(
-                GraphQLString,
-                args={
-                    "numArg": GraphQLArgument(GraphQLInt),
-                    "stringArg": GraphQLArgument(GraphQLString),
-                },
-                resolver=resolver,
-            )
-        },
-    )
-
-    result = execute(GraphQLSchema(Type), doc_ast, None, operation_name="Example")
-    assert not result.errors
-    assert resolver.got_here
-
-
-def test_nulls_out_error_subtrees():
-    # type: () -> None
-    doc = """{
-        ok,
-        error
-    }"""
-
-    class Data(object):
-        def ok(self):
-            # type: () -> str
-            return "ok"
-
-        def error(self):
-            # type: () -> NoReturn
-            raise Exception("Error getting error")
-
-    doc_ast = parse(doc)
-
-    Type = GraphQLObjectType(
-        "Type",
-        {"ok": GraphQLField(GraphQLString), "error": GraphQLField(GraphQLString)},
-    )
-
-    result = execute(GraphQLSchema(Type), doc_ast, Data())
-    assert result.data == {"ok": "ok", "error": None}
-    assert len(result.errors) == 1
-    assert result.errors[0].message == "Error getting error"
-    # TODO: check error location
-
-
-def test_uses_the_inline_operation_if_no_operation_name_is_provided():
-    # type: () -> None
-    doc = "{ a }"
-
-    class Data(object):
-        a = "b"
-
-    ast = parse(doc)
-    Type = GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
-    result = execute(GraphQLSchema(Type), ast, Data())
-    assert not result.errors
-    assert result.data == {"a": "b"}
-
-
-def test_uses_the_only_operation_if_no_operation_name_is_provided():
-    # type: () -> None
-    doc = "query Example { a }"
-
-    class Data(object):
-        a = "b"
-
-    ast = parse(doc)
-    Type = GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
-    result = execute(GraphQLSchema(Type), ast, Data())
-    assert not result.errors
-    assert result.data == {"a": "b"}
-
-
-def test_uses_the_named_operation_if_operation_name_is_provided():
-    # type: () -> None
-    doc = "query Example { first: a } query OtherExample { second: a }"
-
-    class Data(object):
-        a = "b"
-
-    ast = parse(doc)
-    Type = GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
-    result = execute(GraphQLSchema(Type), ast, Data(), operation_name="OtherExample")
-    assert not result.errors
-    assert result.data == {"second": "b"}
-
-
-def test_raises_if_no_operation_is_provided():
-    # type: () -> None
-    doc = "fragment Example on Type { a }"
-
-    class Data(object):
-        a = "b"
-
-    ast = parse(doc)
-    Type = GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
-    with raises(GraphQLError) as excinfo:
-        execute(GraphQLSchema(Type), ast, Data())
-    assert "Must provide an operation." == str(excinfo.value)
-
-
-def test_raises_if_no_operation_name_is_provided_with_multiple_operations():
-    # type: () -> None
-    doc = "query Example { a } query OtherExample { a }"
-
-    class Data(object):
-        a = "b"
-
-    ast = parse(doc)
-    Type = GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
-    with raises(GraphQLError) as excinfo:
-        execute(GraphQLSchema(Type), ast, Data(), operation_name="UnknownExample")
-    assert 'Unknown operation named "UnknownExample".' == str(excinfo.value)
-
-
-def test_raises_if_unknown_operation_name_is_provided():
-    # type: () -> None
-    doc = "query Example { a } query OtherExample { a }"
-
-    class Data(object):
-        a = "b"
-
-    ast = parse(doc)
-    Type = GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
-    with raises(GraphQLError) as excinfo:
-        execute(GraphQLSchema(Type), ast, Data())
-    assert "Must provide operation name if query contains multiple operations." == str(
-        excinfo.value
-    )
-
-
-def test_uses_the_query_schema_for_queries():
-    # type: () -> None
-    doc = "query Q { a } mutation M { c } subscription S { a }"
-
-    class Data(object):
-        a = "b"
-        c = "d"
-
-    ast = parse(doc)
-    Q = GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)})
-    M = GraphQLObjectType("M", {"c": GraphQLField(GraphQLString)})
-    S = GraphQLObjectType("S", {"a": GraphQLField(GraphQLString)})
-    result = execute(GraphQLSchema(Q, M, S), ast, Data(), operation_name="Q")
-    assert not result.errors
-    assert result.data == {"a": "b"}
-
-
-def test_uses_the_mutation_schema_for_queries():
-    # type: () -> None
-    doc = "query Q { a } mutation M { c }"
-
-    class Data(object):
-        a = "b"
-        c = "d"
-
-    ast = parse(doc)
-    Q = GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)})
-    M = GraphQLObjectType("M", {"c": GraphQLField(GraphQLString)})
-    result = execute(GraphQLSchema(Q, M), ast, Data(), operation_name="M")
-    assert not result.errors
-    assert result.data == {"c": "d"}
-
-
-def test_uses_the_subscription_schema_for_subscriptions():
-    # type: () -> None
-    from rx import Observable
-
-    doc = "query Q { a } subscription S { a }"
-
-    class Data(object):
-        a = "b"
-        c = "d"
-
-    ast = parse(doc)
-    Q = GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)})
-    S = GraphQLObjectType(
-        "S",
-        {
-            "a": GraphQLField(
-                GraphQLString, resolver=lambda root, info: Observable.from_(["b"])
-            )
-        },
-    )
-    result = execute(
-        GraphQLSchema(Q, subscription=S),
-        ast,
-        Data(),
-        operation_name="S",
-        allow_subscriptions=True,
-    )
-    assert isinstance(result, Observable)
-    l = []
-    result.subscribe(l.append)
-    result = l[0]
-    assert not result.errors
-    assert result.data == {"a": "b"}
-
-
-def test_avoids_recursion():
-    # type: () -> None
-    doc = """
-        query Q {
-            a
-            ...Frag
-            ...Frag
-        }
-        fragment Frag on Type {
-            a,
-            ...Frag
-        }
-    """
-
-    class Data(object):
-        a = "b"
-
-    ast = parse(doc)
-    Type = GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
-    result = execute(GraphQLSchema(Type), ast, Data(), operation_name="Q")
-    assert not result.errors
-    assert result.data == {"a": "b"}
-
-
-def test_does_not_include_illegal_fields_in_output():
-    # type: () -> None
-    doc = "mutation M { thisIsIllegalDontIncludeMe }"
-    ast = parse(doc)
-    Q = GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)})
-    M = GraphQLObjectType("M", {"c": GraphQLField(GraphQLString)})
-    result = execute(GraphQLSchema(Q, M), ast)
-    assert not result.errors
-    assert result.data == {}
-
-
-def test_does_not_include_arguments_that_were_not_set():
-    # type: () -> None
-    schema = GraphQLSchema(
-        GraphQLObjectType(
-            "Type",
-            {
-                "field": GraphQLField(
-                    GraphQLString,
-                    resolver=lambda source, info, **args: args
-                    and json.dumps(args, sort_keys=True, separators=(",", ":")),
-                    args={
-                        "a": GraphQLArgument(GraphQLBoolean),
-                        "b": GraphQLArgument(GraphQLBoolean),
-                        "c": GraphQLArgument(GraphQLBoolean),
-                        "d": GraphQLArgument(GraphQLInt),
-                        "e": GraphQLArgument(GraphQLInt),
-                    },
-                )
-            },
-        )
-    )
-
-    ast = parse("{ field(a: true, c: false, e: 0) }")
-    result = execute(schema, ast)
-    assert result.data == {"field": '{"a":true,"c":false,"e":0}'}
-
-
-def test_fails_when_an_is_type_of_check_is_not_met():
-    # type: () -> None
-    class Special(object):
-        def __init__(self, value):
-            # type: (str) -> None
-            self.value = value
-
-    class NotSpecial(object):
-        def __init__(self, value):
-            # type: (str) -> None
-            self.value = value
-
-    SpecialType = GraphQLObjectType(
-        "SpecialType",
-        fields={"value": GraphQLField(GraphQLString)},
-        is_type_of=lambda obj, info: isinstance(obj, Special),
-    )
-
-    schema = GraphQLSchema(
-        GraphQLObjectType(
-            name="Query",
-            fields={
-                "specials": GraphQLField(
-                    GraphQLList(SpecialType), resolver=lambda root, *_: root["specials"]
-                )
-            },
-        )
-    )
-
-    query = parse("{ specials { value } }")
-    value = {"specials": [Special("foo"), NotSpecial("bar")]}
-
-    result = execute(schema, query, value)
-
-    assert result.data == {"specials": [{"value": "foo"}, None]}
-
-    assert 'Expected value of type "SpecialType" but got: NotSpecial.' in [
-        str(e) for e in result.errors
-    ]
-
-
-def test_fails_to_execute_a_query_containing_a_type_definition():
-    # type: () -> None
-    query = parse(
-        """
-    { foo }
-
-    type Query { foo: String }
-    """
-    )
-
-    schema = GraphQLSchema(
-        GraphQLObjectType(name="Query", fields={"foo": GraphQLField(GraphQLString)})
-    )
-
-    with raises(GraphQLError) as excinfo:
-        execute(schema, query)
-
-    assert (
-        excinfo.value.message
-        == "GraphQL cannot execute a request containing a ObjectTypeDefinition."
-    )
-
-
-def test_exceptions_are_reraised_if_specified(mocker):
-    # type: (MockFixture) -> None
-
-    logger = mocker.patch("graphql.execution.executor.logger")
-
-    query = parse(
-        """
-    { foo }
-    """
-    )
-
-    def resolver(*_):
-        # type: (*Any) -> NoReturn
-        raise Exception("UH OH!")
-
-    schema = GraphQLSchema(
-        GraphQLObjectType(
-            name="Query", fields={"foo": GraphQLField(GraphQLString, resolver=resolver)}
-        )
-    )
-
-    execute(schema, query)
-    logger.exception.assert_called_with(
-        "An error occurred while resolving field Query.foo"
-    )
-
-
-def test_executor_properly_propogates_path_data(mocker):
-    # type: (MockFixture) -> None
-    time_mock = mocker.patch("time.time")
-    time_mock.side_effect = range(0, 10000)
-
-    BlogImage = GraphQLObjectType(
-        "BlogImage",
-        {
-            "url": GraphQLField(GraphQLString),
-            "width": GraphQLField(GraphQLInt),
-            "height": GraphQLField(GraphQLInt),
-        },
-    )
-
-    BlogAuthor = GraphQLObjectType(
-        "Author",
-        lambda: {
-            "id": GraphQLField(GraphQLString),
-            "name": GraphQLField(GraphQLString),
-            "pic": GraphQLField(
-                BlogImage,
-                args={
-                    "width": GraphQLArgument(GraphQLInt),
-                    "height": GraphQLArgument(GraphQLInt),
-                },
-                resolver=lambda obj, info, **args: obj.pic(
-                    args["width"], args["height"]
-                ),
-            ),
-            "recentArticle": GraphQLField(BlogArticle),
-        },
-    )
-
-    BlogArticle = GraphQLObjectType(
-        "Article",
-        {
-            "id": GraphQLField(GraphQLNonNull(GraphQLString)),
-            "isPublished": GraphQLField(GraphQLBoolean),
-            "author": GraphQLField(BlogAuthor),
-            "title": GraphQLField(GraphQLString),
-            "body": GraphQLField(GraphQLString),
-            "keywords": GraphQLField(GraphQLList(GraphQLString)),
-        },
-    )
-
-    BlogQuery = GraphQLObjectType(
-        "Query",
-        {
-            "article": GraphQLField(
-                BlogArticle,
-                args={"id": GraphQLArgument(GraphQLID)},
-                resolver=lambda obj, info, **args: Article(args["id"]),
-            ),
-            "feed": GraphQLField(
-                GraphQLList(BlogArticle),
-                resolver=lambda *_: map(Article, range(1, 2 + 1)),
-            ),
-        },
-    )
-
-    BlogSchema = GraphQLSchema(BlogQuery)
-
-    class Article(object):
-        def __init__(self, id):
-            # type: (int) -> None
-            self.id = id
-            self.isPublished = True
-            self.author = Author()
-            self.title = "My Article {}".format(id)
-            self.body = "This is a post"
-            self.hidden = "This data is not exposed in the schema"
-            self.keywords = ["foo", "bar", 1, True, None]
-
-    class Author(object):
-        id = 123
-        name = "John Smith"
-
-        def pic(self, width, height):
-            return Pic(123, width, height)
-
-        @property
-        def recentArticle(self):
-            return Article(1)
-
-    class Pic(object):
-        def __init__(self, uid, width, height):
-            self.url = "cdn://{}".format(uid)
-            self.width = str(width)
-            self.height = str(height)
-
-    class PathCollectorMiddleware(object):
-        def __init__(self):
-            # type: () -> None
-            self.paths = []
-
-        def resolve(
-            self,
-            _next,  # type: Callable
-            root,  # type: Optional[Article]
-            info,  # type: ResolveInfo
-            *args,  # type: Any
-            **kwargs  # type: Any
-        ):
-            # type: (...) -> Promise
-            self.paths.append(info.path)
-            return _next(root, info, *args, **kwargs)
-
-    request = """
-    {
-        feed {
-          id
-          ...articleFields
-          author {
-            id
-            name
-            nameAlias: name
-          }
-        },
-    }
-    fragment articleFields on Article {
-        title,
-        body,
-        hidden,
-    }
-    """
-
-    paths_middleware = PathCollectorMiddleware()
-
-    result = execute(BlogSchema, parse(request), middleware=(paths_middleware,))
-    assert not result.errors
-    assert result.data == {
-        "feed": [
-            {
-                "id": "1",
-                "title": "My Article 1",
-                "body": "This is a post",
-                "author": {
-                    "id": "123",
-                    "name": "John Smith",
-                    "nameAlias": "John Smith",
-                },
-            },
-            {
-                "id": "2",
-                "title": "My Article 2",
-                "body": "This is a post",
-                "author": {
-                    "id": "123",
-                    "name": "John Smith",
-                    "nameAlias": "John Smith",
-                },
-            },
-        ]
-    }
-
-    traversed_paths = paths_middleware.paths
-    assert traversed_paths == [
-        ["feed"],
-        ["feed", 0, "id"],
-        ["feed", 0, "title"],
-        ["feed", 0, "body"],
-        ["feed", 0, "author"],
-        ["feed", 1, "id"],
-        ["feed", 1, "title"],
-        ["feed", 1, "body"],
-        ["feed", 1, "author"],
-        ["feed", 0, "author", "id"],
-        ["feed", 0, "author", "name"],
-        ["feed", 0, "author", "nameAlias"],
-        ["feed", 1, "author", "id"],
-        ["feed", 1, "author", "name"],
-        ["feed", 1, "author", "nameAlias"],
-    ]
diff --git a/graphql/execution/tests/test_executor_asyncio.py b/graphql/execution/tests/test_executor_asyncio.py
deleted file mode 100644
index 714f59e..0000000
--- a/graphql/execution/tests/test_executor_asyncio.py
+++ /dev/null
@@ -1,122 +0,0 @@
-"""
-   isort:skip_file
-"""
-# type: ignore
-# flake8: noqa
-
-import pytest
-
-asyncio = pytest.importorskip("asyncio")
-
-from graphql.error import format_error
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
-
-from ..executors.asyncio import AsyncioExecutor
-from .test_mutations import assert_evaluate_mutations_serially
-
-
-def test_asyncio_executor():
-    # type: () -> None
-    def resolver(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> str
-        asyncio.sleep(0.001)
-        return "hey"
-
-    @asyncio.coroutine
-    def resolver_2(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> str
-        asyncio.sleep(0.003)
-        return "hey2"
-
-    def resolver_3(contest, *_):
-        # type: (Optional[Any], *ResolveInfo) -> str
-        return "hey3"
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "a": GraphQLField(GraphQLString, resolver=resolver),
-            "b": GraphQLField(GraphQLString, resolver=resolver_2),
-            "c": GraphQLField(GraphQLString, resolver=resolver_3),
-        },
-    )
-
-    ast = parse("{ a b c }")
-    result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor())
-    assert not result.errors
-    assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}
-
-
-def test_asyncio_executor_custom_loop():
-    # type: () -> None
-    loop = asyncio.get_event_loop()
-
-    def resolver(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> str
-        asyncio.sleep(0.001, loop=loop)
-        return "hey"
-
-    @asyncio.coroutine
-    def resolver_2(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> str
-        asyncio.sleep(0.003, loop=loop)
-        return "hey2"
-
-    def resolver_3(contest, *_):
-        # type: (Optional[Any], *ResolveInfo) -> str
-        return "hey3"
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "a": GraphQLField(GraphQLString, resolver=resolver),
-            "b": GraphQLField(GraphQLString, resolver=resolver_2),
-            "c": GraphQLField(GraphQLString, resolver=resolver_3),
-        },
-    )
-
-    ast = parse("{ a b c }")
-    result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor(loop=loop))
-    assert not result.errors
-    assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}
-
-
-def test_asyncio_executor_with_error():
-    # type: () -> None
-    ast = parse("query Example { a, b }")
-
-    def resolver(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> str
-        asyncio.sleep(0.001)
-        return "hey"
-
-    def resolver_2(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> NoReturn
-        asyncio.sleep(0.003)
-        raise Exception("resolver_2 failed!")
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "a": GraphQLField(GraphQLString, resolver=resolver),
-            "b": GraphQLField(GraphQLString, resolver=resolver_2),
-        },
-    )
-
-    result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor())
-    formatted_errors = list(map(format_error, result.errors))
-    assert formatted_errors == [
-        {
-            "locations": [{"line": 1, "column": 20}],
-            "path": ["b"],
-            "message": "resolver_2 failed!",
-        }
-    ]
-    assert result.data == {"a": "hey", "b": None}
-
-
-def test_evaluates_mutations_serially():
-    # type: () -> None
-    assert_evaluate_mutations_serially(executor=AsyncioExecutor())
diff --git a/graphql/execution/tests/test_executor_gevent.py b/graphql/execution/tests/test_executor_gevent.py
deleted file mode 100644
index 9256a01..0000000
--- a/graphql/execution/tests/test_executor_gevent.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""
-   isort:skip_file
-"""
-# type: ignore
-# flake8: noqa
-
-import pytest
-
-gevent = pytest.importorskip("gevent")
-
-from graphql.error import format_error
-from graphql.execution import execute
-from graphql.language.location import SourceLocation
-from graphql.language.parser import parse
-from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
-
-from ..executors.gevent import GeventExecutor
-from .test_mutations import assert_evaluate_mutations_serially
-
-
-def test_gevent_executor():
-    def resolver(context, *_):
-        gevent.sleep(0.001)
-        return "hey"
-
-    def resolver_2(context, *_):
-        gevent.sleep(0.003)
-        return "hey2"
-
-    def resolver_3(contest, *_):
-        return "hey3"
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "a": GraphQLField(GraphQLString, resolver=resolver),
-            "b": GraphQLField(GraphQLString, resolver=resolver_2),
-            "c": GraphQLField(GraphQLString, resolver=resolver_3),
-        },
-    )
-
-    ast = parse("{ a b c }")
-    result = execute(GraphQLSchema(Type), ast, executor=GeventExecutor())
-    assert not result.errors
-    assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}
-
-
-def test_gevent_executor_with_error():
-    ast = parse("query Example { a, b }")
-
-    def resolver(context, *_):
-        gevent.sleep(0.001)
-        return "hey"
-
-    def resolver_2(context, *_):
-        gevent.sleep(0.003)
-        raise Exception("resolver_2 failed!")
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "a": GraphQLField(GraphQLString, resolver=resolver),
-            "b": GraphQLField(GraphQLString, resolver=resolver_2),
-        },
-    )
-
-    result = execute(GraphQLSchema(Type), ast, executor=GeventExecutor())
-    formatted_errors = list(map(format_error, result.errors))
-    assert formatted_errors == [
-        {
-            "locations": [{"line": 1, "column": 20}],
-            "path": ["b"],
-            "message": "resolver_2 failed!",
-        }
-    ]
-    assert result.data == {"a": "hey", "b": None}
-
-
-def test_evaluates_mutations_serially():
-    assert_evaluate_mutations_serially(executor=GeventExecutor())
diff --git a/graphql/execution/tests/test_executor_thread.py b/graphql/execution/tests/test_executor_thread.py
deleted file mode 100644
index ceaf8ce..0000000
--- a/graphql/execution/tests/test_executor_thread.py
+++ /dev/null
@@ -1,294 +0,0 @@
-# type: ignore
-from graphql.error import format_error
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLField,
-    GraphQLInt,
-    GraphQLList,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-
-from ..executors.thread import ThreadExecutor
-from .test_mutations import assert_evaluate_mutations_serially
-from .utils import rejected, resolved
-
-
-def test_executes_arbitary_code():
-    # type: () -> None
-    class Data(object):
-        a = "Apple"
-        b = "Banana"
-        c = "Cookie"
-        d = "Donut"
-        e = "Egg"
-
-        @property
-        def f(self):
-            # type: () -> Promise
-            return resolved("Fish")
-
-        def pic(self, size=50):
-            # type: (int) -> Promise
-            return resolved("Pic of size: {}".format(size))
-
-        def deep(self):
-            # type: () -> DeepData
-            return DeepData()
-
-        def promise(self):
-            # type: () -> Promise
-            return resolved(Data())
-
-    class DeepData(object):
-        a = "Already Been Done"
-        b = "Boring"
-        c = ["Contrived", None, resolved("Confusing")]
-
-        def deeper(self):
-            # type: () -> List[Union[None, Data, Promise]]
-            return [Data(), None, resolved(Data())]
-
-    ast = parse(
-        """
-        query Example($size: Int) {
-            a,
-            b,
-            x: c
-            ...c
-            f
-            ...on DataType {
-                pic(size: $size)
-                promise {
-                    a
-                }
-            }
-            deep {
-                a
-                b
-                c
-                deeper {
-                    a
-                    b
-                }
-            }
-        }
-        fragment c on DataType {
-            d
-            e
-        }
-    """
-    )
-
-    expected = {
-        "a": "Apple",
-        "b": "Banana",
-        "x": "Cookie",
-        "d": "Donut",
-        "e": "Egg",
-        "f": "Fish",
-        "pic": "Pic of size: 100",
-        "promise": {"a": "Apple"},
-        "deep": {
-            "a": "Already Been Done",
-            "b": "Boring",
-            "c": ["Contrived", None, "Confusing"],
-            "deeper": [
-                {"a": "Apple", "b": "Banana"},
-                None,
-                {"a": "Apple", "b": "Banana"},
-            ],
-        },
-    }
-
-    DataType = GraphQLObjectType(
-        "DataType",
-        lambda: {
-            "a": GraphQLField(GraphQLString),
-            "b": GraphQLField(GraphQLString),
-            "c": GraphQLField(GraphQLString),
-            "d": GraphQLField(GraphQLString),
-            "e": GraphQLField(GraphQLString),
-            "f": GraphQLField(GraphQLString),
-            "pic": GraphQLField(
-                args={"size": GraphQLArgument(GraphQLInt)},
-                type=GraphQLString,
-                resolver=lambda obj, info, **args: obj.pic(args["size"]),
-            ),
-            "deep": GraphQLField(DeepDataType),
-            "promise": GraphQLField(DataType),
-        },
-    )
-
-    DeepDataType = GraphQLObjectType(
-        "DeepDataType",
-        {
-            "a": GraphQLField(GraphQLString),
-            "b": GraphQLField(GraphQLString),
-            "c": GraphQLField(GraphQLList(GraphQLString)),
-            "deeper": GraphQLField(GraphQLList(DataType)),
-        },
-    )
-
-    schema = GraphQLSchema(query=DataType)
-
-    def handle_result(result):
-        # type: (ExecutionResult) -> None
-        assert not result.errors
-        assert result.data == expected
-
-    handle_result(
-        execute(
-            schema,
-            ast,
-            Data(),
-            variable_values={"size": 100},
-            operation_name="Example",
-            executor=ThreadExecutor(),
-        )
-    )
-    handle_result(
-        execute(
-            schema, ast, Data(), variable_values={"size": 100}, operation_name="Example"
-        )
-    )
-
-
-def test_synchronous_error_nulls_out_error_subtrees():
-    # type: () -> None
-    ast = parse(
-        """
-    {
-        sync
-        syncError
-        syncReturnError
-        syncReturnErrorList
-        asyncBasic
-        asyncReject
-        asyncEmptyReject
-        asyncReturnError
-    }
-    """
-    )
-
-    class Data:
-        def sync(self):
-            # type: () -> str
-            return "sync"
-
-        def syncError(self):
-            # type: () -> NoReturn
-            raise Exception("Error getting syncError")
-
-        def syncReturnError(self):
-            # type: () -> Exception
-            return Exception("Error getting syncReturnError")
-
-        def syncReturnErrorList(self):
-            # type: () -> List[Union[Exception, str]]
-            return [
-                "sync0",
-                Exception("Error getting syncReturnErrorList1"),
-                "sync2",
-                Exception("Error getting syncReturnErrorList3"),
-            ]
-
-        def asyncBasic(self):
-            # type: () -> Promise
-            return resolved("async")
-
-        def asyncReject(self):
-            # type: () -> Promise
-            return rejected(Exception("Error getting asyncReject"))
-
-        def asyncEmptyReject(self):
-            # type: () -> Promise
-            return rejected(Exception("An unknown error occurred."))
-
-        def asyncReturnError(self):
-            # type: () -> Promise
-            return resolved(Exception("Error getting asyncReturnError"))
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Type",
-            fields={
-                "sync": GraphQLField(GraphQLString),
-                "syncError": GraphQLField(GraphQLString),
-                "syncReturnError": GraphQLField(GraphQLString),
-                "syncReturnErrorList": GraphQLField(GraphQLList(GraphQLString)),
-                "asyncBasic": GraphQLField(GraphQLString),
-                "asyncReject": GraphQLField(GraphQLString),
-                "asyncEmptyReject": GraphQLField(GraphQLString),
-                "asyncReturnError": GraphQLField(GraphQLString),
-            },
-        )
-    )
-
-    def sort_key(item):
-        # type: (Dict[str, Any]) -> Tuple[int, int]
-        locations = item["locations"][0]
-        return (locations["line"], locations["column"])
-
-    def handle_results(result):
-        # type: (ExecutionResult) -> None
-        assert result.data == {
-            "asyncBasic": "async",
-            "asyncEmptyReject": None,
-            "asyncReject": None,
-            "asyncReturnError": None,
-            "sync": "sync",
-            "syncError": None,
-            "syncReturnError": None,
-            "syncReturnErrorList": ["sync0", None, "sync2", None],
-        }
-        assert sorted(list(map(format_error, result.errors)), key=sort_key) == sorted(
-            [
-                {
-                    "locations": [{"line": 4, "column": 9}],
-                    "path": ["syncError"],
-                    "message": "Error getting syncError",
-                },
-                {
-                    "locations": [{"line": 5, "column": 9}],
-                    "path": ["syncReturnError"],
-                    "message": "Error getting syncReturnError",
-                },
-                {
-                    "locations": [{"line": 6, "column": 9}],
-                    "path": ["syncReturnErrorList", 1],
-                    "message": "Error getting syncReturnErrorList1",
-                },
-                {
-                    "locations": [{"line": 6, "column": 9}],
-                    "path": ["syncReturnErrorList", 3],
-                    "message": "Error getting syncReturnErrorList3",
-                },
-                {
-                    "locations": [{"line": 8, "column": 9}],
-                    "path": ["asyncReject"],
-                    "message": "Error getting asyncReject",
-                },
-                {
-                    "locations": [{"line": 9, "column": 9}],
-                    "path": ["asyncEmptyReject"],
-                    "message": "An unknown error occurred.",
-                },
-                {
-                    "locations": [{"line": 10, "column": 9}],
-                    "path": ["asyncReturnError"],
-                    "message": "Error getting asyncReturnError",
-                },
-            ],
-            key=sort_key,
-        )
-
-    handle_results(execute(schema, ast, Data(), executor=ThreadExecutor()))
-
-
-def test_evaluates_mutations_serially():
-    # type: () -> None
-    assert_evaluate_mutations_serially(executor=ThreadExecutor())
diff --git a/graphql/execution/tests/test_format_error.py b/graphql/execution/tests/test_format_error.py
deleted file mode 100644
index 67a75f4..0000000
--- a/graphql/execution/tests/test_format_error.py
+++ /dev/null
@@ -1,16 +0,0 @@
-# coding: utf-8
-import pytest
-
-from graphql.error import GraphQLError, format_error
-
-
-@pytest.mark.parametrize(
-    "error",
-    [
-        GraphQLError("UNIÇODÉ!"),
-        GraphQLError("\xd0\xbe\xd1\x88\xd0\xb8\xd0\xb1\xd0\xba\xd0\xb0"),
-    ],
-)
-def test_unicode_format_error(error):
-    # type: (GraphQLError) -> None
-    assert isinstance(format_error(error), dict)
diff --git a/graphql/execution/tests/test_lists.py b/graphql/execution/tests/test_lists.py
deleted file mode 100644
index 3534a13..0000000
--- a/graphql/execution/tests/test_lists.py
+++ /dev/null
@@ -1,389 +0,0 @@
-# type: ignore
-from collections import namedtuple
-
-from graphql.error import format_error
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLField,
-    GraphQLInt,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-)
-
-from .utils import rejected, resolved
-
-Data = namedtuple("Data", "test")
-ast = parse("{ nest { test } }")
-
-
-def check(test_data, expected):
-    def run_check(self):
-        # type: (Any) -> None
-        test_type = self.type
-
-        data = Data(test=test_data)
-        DataType = GraphQLObjectType(
-            name="DataType",
-            fields=lambda: {
-                "test": GraphQLField(test_type),
-                "nest": GraphQLField(DataType, resolver=lambda *_: data),
-            },
-        )
-
-        schema = GraphQLSchema(query=DataType)
-        response = execute(schema, ast, data)
-
-        if response.errors:
-            result = {
-                "data": response.data,
-                "errors": [format_error(e) for e in response.errors],
-            }
-        else:
-            result = {"data": response.data}
-
-        assert result == expected
-
-    return run_check
-
-
-class Test_ListOfT_Array_T:  # [T] Array<T>
-    type = GraphQLList(GraphQLInt)
-
-    test_contains_values = check([1, 2], {"data": {"nest": {"test": [1, 2]}}})
-    test_contains_null = check([1, None, 2], {"data": {"nest": {"test": [1, None, 2]}}})
-    test_returns_null = check(None, {"data": {"nest": {"test": None}}})
-
-
-class Test_ListOfT_Promise_Array_T:  # [T] Promise<Array<T>>
-    type = GraphQLList(GraphQLInt)
-
-    test_contains_values = check(resolved([1, 2]), {"data": {"nest": {"test": [1, 2]}}})
-    test_contains_null = check(
-        resolved([1, None, 2]), {"data": {"nest": {"test": [1, None, 2]}}}
-    )
-    test_returns_null = check(resolved(None), {"data": {"nest": {"test": None}}})
-    test_rejected = check(
-        lambda: rejected(Exception("bad")),
-        {
-            "data": {"nest": {"test": None}},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test"],
-                    "message": "bad",
-                }
-            ],
-        },
-    )
-
-
-class Test_ListOfT_Array_Promise_T:  # [T] Array<Promise<T>>
-    type = GraphQLList(GraphQLInt)
-
-    test_contains_values = check(
-        [resolved(1), resolved(2)], {"data": {"nest": {"test": [1, 2]}}}
-    )
-    test_contains_null = check(
-        [resolved(1), resolved(None), resolved(2)],
-        {"data": {"nest": {"test": [1, None, 2]}}},
-    )
-    test_contains_reject = check(
-        lambda: [resolved(1), rejected(Exception("bad")), resolved(2)],
-        {
-            "data": {"nest": {"test": [1, None, 2]}},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "bad",
-                }
-            ],
-        },
-    )
-
-
-class Test_NotNullListOfT_Array_T:  # [T]! Array<T>
-    type = GraphQLNonNull(GraphQLList(GraphQLInt))
-
-    test_contains_values = check(resolved([1, 2]), {"data": {"nest": {"test": [1, 2]}}})
-    test_contains_null = check(
-        resolved([1, None, 2]), {"data": {"nest": {"test": [1, None, 2]}}}
-    )
-    test_returns_null = check(
-        resolved(None),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test"],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-
-
-class Test_NotNullListOfT_Promise_Array_T:  # [T]! Promise<Array<T>>>
-    type = GraphQLNonNull(GraphQLList(GraphQLInt))
-
-    test_contains_values = check(resolved([1, 2]), {"data": {"nest": {"test": [1, 2]}}})
-    test_contains_null = check(
-        resolved([1, None, 2]), {"data": {"nest": {"test": [1, None, 2]}}}
-    )
-    test_returns_null = check(
-        resolved(None),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test"],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-
-    test_rejected = check(
-        lambda: rejected(Exception("bad")),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test"],
-                    "message": "bad",
-                }
-            ],
-        },
-    )
-
-
-class Test_NotNullListOfT_Array_Promise_T:  # [T]! Promise<Array<T>>>
-    type = GraphQLNonNull(GraphQLList(GraphQLInt))
-    test_contains_values = check(
-        [resolved(1), resolved(2)], {"data": {"nest": {"test": [1, 2]}}}
-    )
-    test_contains_null = check(
-        [resolved(1), resolved(None), resolved(2)],
-        {"data": {"nest": {"test": [1, None, 2]}}},
-    )
-    test_contains_reject = check(
-        lambda: [resolved(1), rejected(Exception("bad")), resolved(2)],
-        {
-            "data": {"nest": {"test": [1, None, 2]}},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "bad",
-                }
-            ],
-        },
-    )
-
-
-class TestListOfNotNullT_Array_T:  # [T!] Array<T>
-    type = GraphQLList(GraphQLNonNull(GraphQLInt))
-
-    test_contains_values = check([1, 2], {"data": {"nest": {"test": [1, 2]}}})
-    test_contains_null = check(
-        [1, None, 2],
-        {
-            "data": {"nest": {"test": None}},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-    test_returns_null = check(None, {"data": {"nest": {"test": None}}})
-
-
-class TestListOfNotNullT_Promise_Array_T:  # [T!] Promise<Array<T>>
-    type = GraphQLList(GraphQLNonNull(GraphQLInt))
-
-    test_contains_value = check(resolved([1, 2]), {"data": {"nest": {"test": [1, 2]}}})
-    test_contains_null = check(
-        resolved([1, None, 2]),
-        {
-            "data": {"nest": {"test": None}},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-
-    test_returns_null = check(resolved(None), {"data": {"nest": {"test": None}}})
-
-    test_rejected = check(
-        lambda: rejected(Exception("bad")),
-        {
-            "data": {"nest": {"test": None}},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test"],
-                    "message": "bad",
-                }
-            ],
-        },
-    )
-
-
-class TestListOfNotNullT_Array_Promise_T:  # [T!] Array<Promise<T>>
-    type = GraphQLList(GraphQLNonNull(GraphQLInt))
-
-    test_contains_values = check(
-        [resolved(1), resolved(2)], {"data": {"nest": {"test": [1, 2]}}}
-    )
-    test_contains_null = check(
-        [resolved(1), resolved(None), resolved(2)],
-        {
-            "data": {"nest": {"test": None}},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-    test_contains_reject = check(
-        lambda: [resolved(1), rejected(Exception("bad")), resolved(2)],
-        {
-            "data": {"nest": {"test": None}},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "bad",
-                }
-            ],
-        },
-    )
-
-
-class TestNotNullListOfNotNullT_Array_T:  # [T!]! Array<T>
-    type = GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLInt)))
-
-    test_contains_values = check([1, 2], {"data": {"nest": {"test": [1, 2]}}})
-    test_contains_null = check(
-        [1, None, 2],
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-    test_returns_null = check(
-        None,
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test"],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-
-
-class TestNotNullListOfNotNullT_Promise_Array_T:  # [T!]! Promise<Array<T>>
-    type = GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLInt)))
-
-    test_contains_value = check(resolved([1, 2]), {"data": {"nest": {"test": [1, 2]}}})
-    test_contains_null = check(
-        resolved([1, None, 2]),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-
-    test_returns_null = check(
-        resolved(None),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test"],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-
-    test_rejected = check(
-        lambda: rejected(Exception("bad")),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test"],
-                    "message": "bad",
-                }
-            ],
-        },
-    )
-
-
-class TestNotNullListOfNotNullT_Array_Promise_T:  # [T!]! Array<Promise<T>>
-    type = GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLInt)))
-
-    test_contains_values = check(
-        [resolved(1), resolved(2)], {"data": {"nest": {"test": [1, 2]}}}
-    )
-    test_contains_null = check(
-        [resolved(1), resolved(None), resolved(2)],
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "Cannot return null for non-nullable field DataType.test.",
-                }
-            ],
-        },
-    )
-    test_contains_reject = check(
-        lambda: [resolved(1), rejected(Exception("bad")), resolved(2)],
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 10, "line": 1}],
-                    "path": ["nest", "test", 1],
-                    "message": "bad",
-                }
-            ],
-        },
-    )
diff --git a/graphql/execution/tests/test_located_error.py b/graphql/execution/tests/test_located_error.py
deleted file mode 100644
index b5bec97..0000000
--- a/graphql/execution/tests/test_located_error.py
+++ /dev/null
@@ -1,28 +0,0 @@
-# type: ignore
-# coding: utf-8
-
-from graphql import (
-    GraphQLField,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-    execute,
-    parse,
-)
-from graphql.error import GraphQLLocatedError
-
-
-def test_unicode_error_message():
-    # type: () -> None
-    ast = parse("query Example { unicode }")
-
-    def resolver(context, *_):
-        # type: (Optional[Any], *ResolveInfo) -> NoReturn
-        raise Exception(u"UNIÇODÉ!")
-
-    Type = GraphQLObjectType(
-        "Type", {"unicode": GraphQLField(GraphQLString, resolver=resolver)}
-    )
-
-    result = execute(GraphQLSchema(Type), ast)
-    assert isinstance(result.errors[0], GraphQLLocatedError)
diff --git a/graphql/execution/tests/test_middleware.py b/graphql/execution/tests/test_middleware.py
deleted file mode 100644
index 9715288..0000000
--- a/graphql/execution/tests/test_middleware.py
+++ /dev/null
@@ -1,191 +0,0 @@
-# type: ignore
-from __future__ import print_function
-
-import json
-
-from pytest import raises
-from graphql.error import GraphQLError
-from graphql.execution import MiddlewareManager, execute
-from graphql.execution.middleware import get_middleware_resolvers, middleware_chain
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLBoolean,
-    GraphQLField,
-    GraphQLInt,
-    GraphQLList,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLNonNull,
-    GraphQLID,
-)
-from promise import Promise
-
-
-def test_middleware():
-    # type: () -> None
-    doc = """{
-        ok
-        not_ok
-    }"""
-
-    class Data(object):
-        def ok(self):
-            # type: () -> str
-            return "ok"
-
-        def not_ok(self):
-            # type: () -> str
-            return "not_ok"
-
-    doc_ast = parse(doc)
-
-    Type = GraphQLObjectType(
-        "Type",
-        {"ok": GraphQLField(GraphQLString), "not_ok": GraphQLField(GraphQLString)},
-    )
-
-    def reversed_middleware(next, *args, **kwargs):
-        # type: (Callable, *Any, **Any) -> Promise
-        p = next(*args, **kwargs)
-        return p.then(lambda x: x[::-1])
-
-    middlewares = MiddlewareManager(reversed_middleware)
-    result = execute(GraphQLSchema(Type), doc_ast, Data(), middleware=middlewares)
-    assert result.data == {"ok": "ko", "not_ok": "ko_ton"}
-
-
-def test_middleware_class():
-    # type: () -> None
-    doc = """{
-        ok
-        not_ok
-    }"""
-
-    class Data(object):
-        def ok(self):
-            # type: () -> str
-            return "ok"
-
-        def not_ok(self):
-            # type: () -> str
-            return "not_ok"
-
-    doc_ast = parse(doc)
-
-    Type = GraphQLObjectType(
-        "Type",
-        {"ok": GraphQLField(GraphQLString), "not_ok": GraphQLField(GraphQLString)},
-    )
-
-    class MyMiddleware(object):
-        def resolve(self, next, *args, **kwargs):
-            # type: (Callable, *Any, **Any) -> Promise
-            p = next(*args, **kwargs)
-            return p.then(lambda x: x[::-1])
-
-    middlewares = MiddlewareManager(MyMiddleware())
-    result = execute(GraphQLSchema(Type), doc_ast, Data(), middleware=middlewares)
-    assert result.data == {"ok": "ko", "not_ok": "ko_ton"}
-
-
-def test_middleware_skip_promise_wrap():
-    # type: () -> None
-    doc = """{
-        ok
-        not_ok
-    }"""
-
-    class Data(object):
-        def ok(self):
-            # type: () -> str
-            return "ok"
-
-        def not_ok(self):
-            # type: () -> str
-            return "not_ok"
-
-    doc_ast = parse(doc)
-
-    Type = GraphQLObjectType(
-        "Type",
-        {"ok": GraphQLField(GraphQLString), "not_ok": GraphQLField(GraphQLString)},
-    )
-
-    class MyPromiseMiddleware(object):
-        def resolve(self, next, *args, **kwargs):
-            # type: (Callable, *Any, **Any) -> Promise
-            return Promise.resolve(next(*args, **kwargs))
-
-    class MyEmptyMiddleware(object):
-        def resolve(self, next, *args, **kwargs):
-            # type: (Callable, *Any, **Any) -> str
-            return next(*args, **kwargs)
-
-    middlewares_with_promise = MiddlewareManager(
-        MyPromiseMiddleware(), wrap_in_promise=False
-    )
-    middlewares_without_promise = MiddlewareManager(
-        MyEmptyMiddleware(), wrap_in_promise=False
-    )
-
-    result1 = execute(
-        GraphQLSchema(Type), doc_ast, Data(), middleware=middlewares_with_promise
-    )
-    result2 = execute(
-        GraphQLSchema(Type), doc_ast, Data(), middleware=middlewares_without_promise
-    )
-    assert result1.data == result2.data and result1.data == {
-        "ok": "ok",
-        "not_ok": "not_ok",
-    }
-
-
-def test_middleware_chain(capsys):
-    # type: (Any) -> None
-    class CharPrintingMiddleware(object):
-        def __init__(self, char):
-            # type: (str) -> None
-            self.char = char
-
-        def resolve(self, next, *args, **kwargs):
-            # type: (Callable, *Any, **Any) -> str
-            print("resolve() called for middleware {}".format(self.char))
-            return next(*args, **kwargs).then(
-                lambda x: print("then() for {}".format(self.char))
-            )
-
-    middlewares = [
-        CharPrintingMiddleware("a"),
-        CharPrintingMiddleware("b"),
-        CharPrintingMiddleware("c"),
-    ]
-
-    middlewares_resolvers = get_middleware_resolvers(middlewares)
-
-    def func():
-        # type: () -> None
-        return
-
-    chain_iter = middleware_chain(func, middlewares_resolvers, wrap_in_promise=True)
-
-    assert_stdout(capsys, "")
-
-    chain_iter()
-
-    expected_stdout = (
-        "resolve() called for middleware c\n"
-        "resolve() called for middleware b\n"
-        "resolve() called for middleware a\n"
-        "then() for a\n"
-        "then() for b\n"
-        "then() for c\n"
-    )
-    assert_stdout(capsys, expected_stdout)
-
-
-def assert_stdout(capsys, expected_stdout):
-    # type: (Any, str) -> None
-    captured = capsys.readouterr()
-    assert captured.out == expected_stdout
diff --git a/graphql/execution/tests/test_mutations.py b/graphql/execution/tests/test_mutations.py
deleted file mode 100644
index d7ec17b..0000000
--- a/graphql/execution/tests/test_mutations.py
+++ /dev/null
@@ -1,164 +0,0 @@
-# type: ignore
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLField,
-    GraphQLInt,
-    GraphQLList,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-
-# from graphql.execution.executors.asyncio import AsyncioExecutor
-# from graphql.execution.executors.thread import ThreadExecutor
-# from typing import Union
-
-
-class NumberHolder(object):
-    def __init__(self, n):
-        # type: (int) -> None
-        self.theNumber = n
-
-
-class Root(object):
-    def __init__(self, n):
-        # type: (int) -> None
-        self.numberHolder = NumberHolder(n)
-
-    def immediately_change_the_number(self, n):
-        # type: (int) -> NumberHolder
-        self.numberHolder.theNumber = n
-        return self.numberHolder
-
-    def promise_to_change_the_number(self, n):
-        # type: (int) -> NumberHolder
-        # TODO: async
-        return self.immediately_change_the_number(n)
-
-    def fail_to_change_the_number(self, n):
-        # type: (int) -> None
-        raise Exception("Cannot change the number")
-
-    def promise_and_fail_to_change_the_number(self, n):
-        # type: (int) -> None
-        # TODO: async
-        self.fail_to_change_the_number(n)
-
-
-NumberHolderType = GraphQLObjectType(
-    "NumberHolder", {"theNumber": GraphQLField(GraphQLInt)}
-)
-
-QueryType = GraphQLObjectType("Query", {"numberHolder": GraphQLField(NumberHolderType)})
-
-MutationType = GraphQLObjectType(
-    "Mutation",
-    {
-        "immediatelyChangeTheNumber": GraphQLField(
-            NumberHolderType,
-            args={"newNumber": GraphQLArgument(GraphQLInt)},
-            resolver=lambda obj, info, **args: obj.immediately_change_the_number(
-                args["newNumber"]
-            ),
-        ),
-        "promiseToChangeTheNumber": GraphQLField(
-            NumberHolderType,
-            args={"newNumber": GraphQLArgument(GraphQLInt)},
-            resolver=lambda obj, info, **args: obj.promise_to_change_the_number(
-                args["newNumber"]
-            ),
-        ),
-        "failToChangeTheNumber": GraphQLField(
-            NumberHolderType,
-            args={"newNumber": GraphQLArgument(GraphQLInt)},
-            resolver=lambda obj, info, **args: obj.fail_to_change_the_number(
-                args["newNumber"]
-            ),
-        ),
-        "promiseAndFailToChangeTheNumber": GraphQLField(
-            NumberHolderType,
-            args={"newNumber": GraphQLArgument(GraphQLInt)},
-            resolver=lambda obj, info, **args: obj.promise_and_fail_to_change_the_number(
-                args["newNumber"]
-            ),
-        ),
-    },
-)
-
-schema = GraphQLSchema(QueryType, MutationType)
-
-
-def assert_evaluate_mutations_serially(executor=None):
-    # type: (Union[None, AsyncioExecutor, ThreadExecutor]) -> None
-    doc = """mutation M {
-      first: immediatelyChangeTheNumber(newNumber: 1) {
-        theNumber
-      },
-      second: promiseToChangeTheNumber(newNumber: 2) {
-        theNumber
-      },
-      third: immediatelyChangeTheNumber(newNumber: 3) {
-        theNumber
-      }
-      fourth: promiseToChangeTheNumber(newNumber: 4) {
-        theNumber
-      },
-      fifth: immediatelyChangeTheNumber(newNumber: 5) {
-        theNumber
-      }
-    }"""
-    ast = parse(doc)
-    result = execute(schema, ast, Root(6), operation_name="M", executor=executor)
-    assert not result.errors
-    assert result.data == {
-        "first": {"theNumber": 1},
-        "second": {"theNumber": 2},
-        "third": {"theNumber": 3},
-        "fourth": {"theNumber": 4},
-        "fifth": {"theNumber": 5},
-    }
-
-
-def test_evaluates_mutations_serially():
-    # type: () -> None
-    assert_evaluate_mutations_serially()
-
-
-def test_evaluates_mutations_correctly_in_the_presense_of_a_failed_mutation():
-    # type: () -> None
-    doc = """mutation M {
-      first: immediatelyChangeTheNumber(newNumber: 1) {
-        theNumber
-      },
-      second: promiseToChangeTheNumber(newNumber: 2) {
-        theNumber
-      },
-      third: failToChangeTheNumber(newNumber: 3) {
-        theNumber
-      }
-      fourth: promiseToChangeTheNumber(newNumber: 4) {
-        theNumber
-      },
-      fifth: immediatelyChangeTheNumber(newNumber: 5) {
-        theNumber
-      }
-      sixth: promiseAndFailToChangeTheNumber(newNumber: 6) {
-        theNumber
-      }
-    }"""
-    ast = parse(doc)
-    result = execute(schema, ast, Root(6), operation_name="M")
-    assert result.data == {
-        "first": {"theNumber": 1},
-        "second": {"theNumber": 2},
-        "third": None,
-        "fourth": {"theNumber": 4},
-        "fifth": {"theNumber": 5},
-        "sixth": None,
-    }
-    assert len(result.errors) == 2
-    # TODO: check error location
-    assert result.errors[0].message == "Cannot change the number"
-    assert result.errors[1].message == "Cannot change the number"
diff --git a/graphql/execution/tests/test_nonnull.py b/graphql/execution/tests/test_nonnull.py
deleted file mode 100644
index 6f8a372..0000000
--- a/graphql/execution/tests/test_nonnull.py
+++ /dev/null
@@ -1,895 +0,0 @@
-# type: ignore
-from graphql.error import format_error
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLField,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-
-from .utils import rejected, resolved
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from promise import Promise
-    from typing import Any, Optional, Dict, Tuple, Union
-
-sync_error = Exception("sync")
-non_null_sync_error = Exception("nonNullSync")
-promise_error = Exception("promise")
-non_null_promise_error = Exception("nonNullPromise")
-
-
-class ThrowingData(object):
-    def sync(self):
-        # type: () -> None
-        raise sync_error
-
-    def nonNullSync(self):
-        # type: () -> None
-        raise non_null_sync_error
-
-    def promise(self):
-        # type: () -> Promise
-        return rejected(promise_error)
-
-    def nonNullPromise(self):
-        # type: () -> Promise
-        return rejected(non_null_promise_error)
-
-    def nest(self):
-        # type: () -> ThrowingData
-        return ThrowingData()
-
-    def nonNullNest(self):
-        # type: () -> ThrowingData
-        return ThrowingData()
-
-    def promiseNest(self):
-        # type: () -> Promise
-        return resolved(ThrowingData())
-
-    def nonNullPromiseNest(self):
-        # type: () -> Promise
-        return resolved(ThrowingData())
-
-
-class NullingData(object):
-    def sync(self):
-        # type: () -> Optional[Any]
-        return None
-
-    def nonNullSync(self):
-        # type: () -> Optional[Any]
-        return None
-
-    def promise(self):
-        # type: () -> Promise
-        return resolved(None)
-
-    def nonNullPromise(self):
-        # type: () -> Promise
-        return resolved(None)
-
-    def nest(self):
-        # type: () -> NullingData
-        return NullingData()
-
-    def nonNullNest(self):
-        # type: () -> NullingData
-        return NullingData()
-
-    def promiseNest(self):
-        # type: () -> Promise
-        return resolved(NullingData())
-
-    def nonNullPromiseNest(self):
-        # type: () -> Promise
-        return resolved(NullingData())
-
-
-DataType = GraphQLObjectType(
-    "DataType",
-    lambda: {
-        "sync": GraphQLField(GraphQLString),
-        "nonNullSync": GraphQLField(GraphQLNonNull(GraphQLString)),
-        "promise": GraphQLField(GraphQLString),
-        "nonNullPromise": GraphQLField(GraphQLNonNull(GraphQLString)),
-        "nest": GraphQLField(DataType),
-        "nonNullNest": GraphQLField(GraphQLNonNull(DataType)),
-        "promiseNest": GraphQLField(DataType),
-        "nonNullPromiseNest": GraphQLField(GraphQLNonNull(DataType)),
-    },
-)
-
-schema = GraphQLSchema(DataType)
-
-
-def order_errors(error):
-    # type: (Dict[str, Any]) -> Tuple[int, int]
-    locations = error["locations"]
-    return (locations[0]["column"], locations[0]["line"])
-
-
-def check(doc, data, expected):
-    # type: (str, Union[NullingData, ThrowingData], Dict[str, Any]) -> None
-    ast = parse(doc)
-    response = execute(schema, ast, data)
-
-    if response.errors:
-        result = {
-            "data": response.data,
-            "errors": [format_error(e) for e in response.errors],
-        }
-        if result["errors"] != expected["errors"]:
-            assert result["data"] == expected["data"]
-            # Sometimes the fields resolves asynchronously, so
-            # we need to check that the errors are the same, but might be
-            # raised in a different order.
-            assert sorted(result["errors"], key=order_errors) == sorted(
-                expected["errors"], key=order_errors
-            )
-        else:
-            assert result == expected
-    else:
-        result = {"data": response.data}
-
-        assert result == expected
-
-
-def test_nulls_a_nullable_field_that_throws_sync():
-    # type: () -> None
-    doc = """
-        query Q {
-            sync
-        }
-    """
-
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": {"sync": None},
-            "errors": [
-                {
-                    "locations": [{"column": 13, "line": 3}],
-                    "path": ["sync"],
-                    "message": str(sync_error),
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_a_nullable_field_that_throws_in_a_promise():
-    # type: () -> None
-    doc = """
-        query Q {
-            promise
-        }
-    """
-
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": {"promise": None},
-            "errors": [
-                {
-                    "locations": [{"column": 13, "line": 3}],
-                    "path": ["promise"],
-                    "message": str(promise_error),
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_a_sync_returned_object_that_contains_a_non_nullable_field_that_throws():
-    # type: () -> None
-    doc = """
-        query Q {
-            nest {
-                nonNullSync,
-            }
-        }
-    """
-
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 17, "line": 4}],
-                    "path": ["nest", "nonNullSync"],
-                    "message": str(non_null_sync_error),
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_a_synchronously_returned_object_that_contains_a_non_nullable_field_that_throws_in_a_promise():
-    # type: () -> None
-    doc = """
-        query Q {
-            nest {
-                nonNullPromise,
-            }
-        }
-    """
-
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 17, "line": 4}],
-                    "path": ["nest", "nonNullPromise"],
-                    "message": str(non_null_promise_error),
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_an_object_returned_in_a_promise_that_contains_a_non_nullable_field_that_throws_synchronously():
-    # type: () -> None
-    doc = """
-        query Q {
-            promiseNest {
-                nonNullSync,
-            }
-        }
-    """
-
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": {"promiseNest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 17, "line": 4}],
-                    "path": ["promiseNest", "nonNullSync"],
-                    "message": str(non_null_sync_error),
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_an_object_returned_in_a_promise_that_contains_a_non_nullable_field_that_throws_in_a_promise():
-    # type: () -> None
-    doc = """
-        query Q {
-            promiseNest {
-                nonNullPromise,
-            }
-        }
-    """
-
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": {"promiseNest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 17, "line": 4}],
-                    "path": ["promiseNest", "nonNullPromise"],
-                    "message": str(non_null_promise_error),
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_a_complex_tree_of_nullable_fields_that_throw():
-    # type: () -> None
-    doc = """
-      query Q {
-        nest {
-          sync
-          promise
-          nest {
-            sync
-            promise
-          }
-          promiseNest {
-            sync
-            promise
-          }
-        }
-        promiseNest {
-          sync
-          promise
-          nest {
-            sync
-            promise
-          }
-          promiseNest {
-            sync
-            promise
-          }
-        }
-      }
-    """
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": {
-                "nest": {
-                    "nest": {"promise": None, "sync": None},
-                    "promise": None,
-                    "promiseNest": {"promise": None, "sync": None},
-                    "sync": None,
-                },
-                "promiseNest": {
-                    "nest": {"promise": None, "sync": None},
-                    "promise": None,
-                    "promiseNest": {"promise": None, "sync": None},
-                    "sync": None,
-                },
-            },
-            "errors": [
-                {
-                    "locations": [{"column": 11, "line": 4}],
-                    "path": ["nest", "sync"],
-                    "message": str(sync_error),
-                },
-                {
-                    "locations": [{"column": 11, "line": 5}],
-                    "path": ["nest", "promise"],
-                    "message": str(promise_error),
-                },
-                {
-                    "locations": [{"column": 13, "line": 7}],
-                    "path": ["nest", "nest", "sync"],
-                    "message": str(sync_error),
-                },
-                {
-                    "locations": [{"column": 13, "line": 8}],
-                    "path": ["nest", "nest", "promise"],
-                    "message": str(promise_error),
-                },
-                {
-                    "locations": [{"column": 13, "line": 11}],
-                    "path": ["nest", "promiseNest", "sync"],
-                    "message": str(sync_error),
-                },
-                {
-                    "locations": [{"column": 13, "line": 12}],
-                    "path": ["nest", "promiseNest", "promise"],
-                    "message": str(promise_error),
-                },
-                {
-                    "locations": [{"column": 11, "line": 16}],
-                    "path": ["promiseNest", "sync"],
-                    "message": str(sync_error),
-                },
-                {
-                    "locations": [{"column": 11, "line": 17}],
-                    "path": ["promiseNest", "promise"],
-                    "message": str(promise_error),
-                },
-                {
-                    "locations": [{"column": 13, "line": 19}],
-                    "path": ["promiseNest", "nest", "sync"],
-                    "message": str(sync_error),
-                },
-                {
-                    "locations": [{"column": 13, "line": 20}],
-                    "path": ["promiseNest", "nest", "promise"],
-                    "message": str(promise_error),
-                },
-                {
-                    "locations": [{"column": 13, "line": 23}],
-                    "path": ["promiseNest", "promiseNest", "sync"],
-                    "message": str(sync_error),
-                },
-                {
-                    "locations": [{"column": 13, "line": 24}],
-                    "path": ["promiseNest", "promiseNest", "promise"],
-                    "message": str(promise_error),
-                },
-            ],
-        },
-    )
-
-
-def test_nulls_the_first_nullable_object_after_a_field_throws_in_a_long_chain_of_fields_that_are_non_null():
-    # type: () -> None
-    doc = """
-    query Q {
-        nest {
-          nonNullNest {
-            nonNullPromiseNest {
-              nonNullNest {
-                nonNullPromiseNest {
-                  nonNullSync
-                }
-              }
-            }
-          }
-        }
-        promiseNest {
-          nonNullNest {
-            nonNullPromiseNest {
-              nonNullNest {
-                nonNullPromiseNest {
-                  nonNullSync
-                }
-              }
-            }
-          }
-        }
-        anotherNest: nest {
-          nonNullNest {
-            nonNullPromiseNest {
-              nonNullNest {
-                nonNullPromiseNest {
-                  nonNullPromise
-                }
-              }
-            }
-          }
-        }
-        anotherPromiseNest: promiseNest {
-          nonNullNest {
-            nonNullPromiseNest {
-              nonNullNest {
-                nonNullPromiseNest {
-                  nonNullPromise
-                }
-              }
-            }
-          }
-        }
-      }
-    """
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": {
-                "nest": None,
-                "promiseNest": None,
-                "anotherNest": None,
-                "anotherPromiseNest": None,
-            },
-            "errors": [
-                {
-                    "locations": [{"column": 19, "line": 8}],
-                    "path": [
-                        "nest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullSync",
-                    ],
-                    "message": str(non_null_sync_error),
-                },
-                {
-                    "locations": [{"column": 19, "line": 19}],
-                    "path": [
-                        "promiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullSync",
-                    ],
-                    "message": str(non_null_sync_error),
-                },
-                {
-                    "locations": [{"column": 19, "line": 30}],
-                    "path": [
-                        "anotherNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullPromise",
-                    ],
-                    "message": str(non_null_promise_error),
-                },
-                {
-                    "locations": [{"column": 19, "line": 41}],
-                    "path": [
-                        "anotherPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullPromise",
-                    ],
-                    "message": str(non_null_promise_error),
-                },
-            ],
-        },
-    )
-
-
-def test_nulls_a_nullable_field_that_returns_null():
-    # type: () -> None
-    doc = """
-        query Q {
-            sync
-        }
-    """
-
-    check(doc, NullingData(), {"data": {"sync": None}})
-
-
-def test_nulls_a_nullable_field_that_returns_null_in_a_promise():
-    # type: () -> None
-    doc = """
-        query Q {
-            promise
-        }
-    """
-
-    check(doc, NullingData(), {"data": {"promise": None}})
-
-
-def test_nulls_a_sync_returned_object_that_contains_a_non_nullable_field_that_returns_null_synchronously():
-    # type: () -> None
-    doc = """
-        query Q {
-            nest {
-                nonNullSync,
-            }
-        }
-    """
-    check(
-        doc,
-        NullingData(),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 17, "line": 4}],
-                    "path": ["nest", "nonNullSync"],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullSync.",
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_a_synchronously_returned_object_that_contains_a_non_nullable_field_that_returns_null_in_a_promise():
-    # type: () -> None
-    doc = """
-        query Q {
-            nest {
-                nonNullPromise,
-            }
-        }
-    """
-    check(
-        doc,
-        NullingData(),
-        {
-            "data": {"nest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 17, "line": 4}],
-                    "path": ["nest", "nonNullPromise"],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullPromise.",
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_an_object_returned_in_a_promise_that_contains_a_non_nullable_field_that_returns_null_synchronously():
-    # type: () -> None
-    doc = """
-        query Q {
-            promiseNest {
-                nonNullSync,
-            }
-        }
-    """
-    check(
-        doc,
-        NullingData(),
-        {
-            "data": {"promiseNest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 17, "line": 4}],
-                    "path": ["promiseNest", "nonNullSync"],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullSync.",
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_an_object_returned_in_a_promise_that_contains_a_non_nullable_field_that_returns_null_ina_a_promise():
-    # type: () -> None
-    doc = """
-        query Q {
-            promiseNest {
-                nonNullPromise
-            }
-        }
-    """
-
-    check(
-        doc,
-        NullingData(),
-        {
-            "data": {"promiseNest": None},
-            "errors": [
-                {
-                    "locations": [{"column": 17, "line": 4}],
-                    "path": ["promiseNest", "nonNullPromise"],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullPromise.",
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_a_complex_tree_of_nullable_fields_that_returns_null():
-    # type: () -> None
-    doc = """
-      query Q {
-        nest {
-          sync
-          promise
-          nest {
-            sync
-            promise
-          }
-          promiseNest {
-            sync
-            promise
-          }
-        }
-        promiseNest {
-          sync
-          promise
-          nest {
-            sync
-            promise
-          }
-          promiseNest {
-            sync
-            promise
-          }
-        }
-      }
-    """
-    check(
-        doc,
-        NullingData(),
-        {
-            "data": {
-                "nest": {
-                    "sync": None,
-                    "promise": None,
-                    "nest": {"sync": None, "promise": None},
-                    "promiseNest": {"sync": None, "promise": None},
-                },
-                "promiseNest": {
-                    "sync": None,
-                    "promise": None,
-                    "nest": {"sync": None, "promise": None},
-                    "promiseNest": {"sync": None, "promise": None},
-                },
-            }
-        },
-    )
-
-
-def test_nulls_the_first_nullable_object_after_a_field_returns_null_in_a_long_chain_of_fields_that_are_non_null():
-    # type: () -> None
-    doc = """
-      query Q {
-        nest {
-          nonNullNest {
-            nonNullPromiseNest {
-              nonNullNest {
-                nonNullPromiseNest {
-                  nonNullSync
-                }
-              }
-            }
-          }
-        }
-        promiseNest {
-          nonNullNest {
-            nonNullPromiseNest {
-              nonNullNest {
-                nonNullPromiseNest {
-                  nonNullSync
-                }
-              }
-            }
-          }
-        }
-        anotherNest: nest {
-          nonNullNest {
-            nonNullPromiseNest {
-              nonNullNest {
-                nonNullPromiseNest {
-                  nonNullPromise
-                }
-              }
-            }
-          }
-        }
-        anotherPromiseNest: promiseNest {
-          nonNullNest {
-            nonNullPromiseNest {
-              nonNullNest {
-                nonNullPromiseNest {
-                  nonNullPromise
-                }
-              }
-            }
-          }
-        }
-      }
-    """
-
-    check(
-        doc,
-        NullingData(),
-        {
-            "data": {
-                "nest": None,
-                "promiseNest": None,
-                "anotherNest": None,
-                "anotherPromiseNest": None,
-            },
-            "errors": [
-                {
-                    "locations": [{"column": 19, "line": 8}],
-                    "path": [
-                        "nest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullSync",
-                    ],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullSync.",
-                },
-                {
-                    "locations": [{"column": 19, "line": 19}],
-                    "path": [
-                        "promiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullSync",
-                    ],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullSync.",
-                },
-                {
-                    "locations": [{"column": 19, "line": 30}],
-                    "path": [
-                        "anotherNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullPromise",
-                    ],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullPromise.",
-                },
-                {
-                    "locations": [{"column": 19, "line": 41}],
-                    "path": [
-                        "anotherPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullNest",
-                        "nonNullPromiseNest",
-                        "nonNullPromise",
-                    ],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullPromise.",
-                },
-            ],
-        },
-    )
-
-
-def test_nulls_the_top_level_if_sync_non_nullable_field_throws():
-    # type: () -> None
-    doc = """
-        query Q { nonNullSync }
-    """
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": None,
-            "errors": [
-                {
-                    "locations": [{"column": 19, "line": 2}],
-                    "path": ["nonNullSync"],
-                    "message": str(non_null_sync_error),
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_the_top_level_if_async_non_nullable_field_errors():
-    # type: () -> None
-    doc = """
-        query Q { nonNullPromise }
-    """
-
-    check(
-        doc,
-        ThrowingData(),
-        {
-            "data": None,
-            "errors": [
-                {
-                    "locations": [{"column": 19, "line": 2}],
-                    "path": ["nonNullPromise"],
-                    "message": str(non_null_promise_error),
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_the_top_level_if_sync_non_nullable_field_returns_null():
-    # type: () -> None
-    doc = """
-        query Q { nonNullSync }
-    """
-    check(
-        doc,
-        NullingData(),
-        {
-            "data": None,
-            "errors": [
-                {
-                    "locations": [{"column": 19, "line": 2}],
-                    "path": ["nonNullSync"],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullSync.",
-                }
-            ],
-        },
-    )
-
-
-def test_nulls_the_top_level_if_async_non_nullable_field_resolves_null():
-    # type: () -> None
-    doc = """
-        query Q { nonNullPromise }
-    """
-    check(
-        doc,
-        NullingData(),
-        {
-            "data": None,
-            "errors": [
-                {
-                    "locations": [{"column": 19, "line": 2}],
-                    "path": ["nonNullPromise"],
-                    "message": "Cannot return null for non-nullable field DataType.nonNullPromise.",
-                }
-            ],
-        },
-    )
diff --git a/graphql/execution/tests/test_resolve.py b/graphql/execution/tests/test_resolve.py
deleted file mode 100644
index 8501558..0000000
--- a/graphql/execution/tests/test_resolve.py
+++ /dev/null
@@ -1,233 +0,0 @@
-# type: ignore
-import json
-from collections import OrderedDict
-
-from graphql import graphql
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLField,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInt,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-from promise import Promise
-
-# from graphql.execution.base import ResolveInfo
-# from typing import Any
-# from typing import Optional
-# from promise.promise import Promise
-# from graphql.type.definition import GraphQLField
-# from graphql.type.schema import GraphQLSchema
-
-
-class CustomPromise(Promise):
-    @classmethod
-    def fulfilled(cls, x):
-        # type: (str) -> CustomPromise
-        p = cls()
-        p.fulfill(x)
-        return p
-
-    resolve = fulfilled
-
-    @classmethod
-    def rejected(cls, reason):
-        p = cls()
-        p.reject(reason)
-        return p
-
-
-def _test_schema(test_field):
-    # type: (GraphQLField) -> GraphQLSchema
-    return GraphQLSchema(
-        query=GraphQLObjectType(name="Query", fields={"test": test_field})
-    )
-
-
-def test_default_function_accesses_properties():
-    # type: () -> None
-    schema = _test_schema(GraphQLField(GraphQLString))
-
-    class source:
-        test = "testValue"
-
-    result = graphql(schema, "{ test }", source)
-    assert not result.errors
-    assert result.data == {"test": "testValue"}
-
-
-def test_default_function_calls_methods():
-    # type: () -> None
-    schema = _test_schema(GraphQLField(GraphQLString))
-
-    class source:
-        _secret = "testValue"
-
-        def test(self):
-            # type: () -> str
-            return self._secret
-
-    result = graphql(schema, "{ test }", source())
-    assert not result.errors
-    assert result.data == {"test": "testValue"}
-
-
-def test_uses_provided_resolve_function():
-    # type: () -> None
-    def resolver(source, info, **args):
-        # type: (Optional[str], ResolveInfo, **Any) -> str
-        return json.dumps([source, args], separators=(",", ":"))
-
-    schema = _test_schema(
-        GraphQLField(
-            GraphQLString,
-            args=OrderedDict(
-                [
-                    ("aStr", GraphQLArgument(GraphQLString)),
-                    ("aInt", GraphQLArgument(GraphQLInt)),
-                ]
-            ),
-            resolver=resolver,
-        )
-    )
-
-    result = graphql(schema, "{ test }", None)
-    assert not result.errors
-    assert result.data == {"test": "[null,{}]"}
-
-    result = graphql(schema, '{ test(aStr: "String!") }', "Source!")
-    assert not result.errors
-    assert result.data == {"test": '["Source!",{"aStr":"String!"}]'}
-
-    result = graphql(schema, '{ test(aInt: -123, aStr: "String!",) }', "Source!")
-    assert not result.errors
-    assert result.data in [
-        {"test": '["Source!",{"aStr":"String!","aInt":-123}]'},
-        {"test": '["Source!",{"aInt":-123,"aStr":"String!"}]'},
-    ]
-
-
-def test_handles_resolved_promises():
-    # type: () -> None
-    def resolver(source, info, **args):
-        # type: (Optional[Any], ResolveInfo, **Any) -> Promise
-        return Promise.resolve("foo")
-
-    schema = _test_schema(GraphQLField(GraphQLString, resolver=resolver))
-
-    result = graphql(schema, "{ test }", None)
-    assert not result.errors
-    assert result.data == {"test": "foo"}
-
-
-def test_handles_resolved_custom_promises():
-    # type: () -> None
-    def resolver(source, info, **args):
-        # type: (Optional[Any], ResolveInfo, **Any) -> CustomPromise
-        return CustomPromise.resolve("custom_foo")
-
-    schema = _test_schema(GraphQLField(GraphQLString, resolver=resolver))
-
-    result = graphql(schema, "{ test }", None)
-    assert not result.errors
-    assert result.data == {"test": "custom_foo"}
-
-
-def test_maps_argument_out_names_well():
-    # type: () -> None
-    def resolver(source, info, **args):
-        # type: (Optional[str], ResolveInfo, **Any) -> str
-        return json.dumps([source, args], separators=(",", ":"))
-
-    schema = _test_schema(
-        GraphQLField(
-            GraphQLString,
-            args=OrderedDict(
-                [
-                    ("aStr", GraphQLArgument(GraphQLString, out_name="a_str")),
-                    ("aInt", GraphQLArgument(GraphQLInt, out_name="a_int")),
-                ]
-            ),
-            resolver=resolver,
-        )
-    )
-
-    result = graphql(schema, "{ test }", None)
-    assert not result.errors
-    assert result.data == {"test": "[null,{}]"}
-
-    result = graphql(schema, '{ test(aStr: "String!") }', "Source!")
-    assert not result.errors
-    assert result.data == {"test": '["Source!",{"a_str":"String!"}]'}
-
-    result = graphql(schema, '{ test(aInt: -123, aStr: "String!",) }', "Source!")
-    assert not result.errors
-    assert result.data in [
-        {"test": '["Source!",{"a_str":"String!","a_int":-123}]'},
-        {"test": '["Source!",{"a_int":-123,"a_str":"String!"}]'},
-    ]
-
-
-def test_maps_argument_out_names_well_with_input():
-    # type: () -> None
-    def resolver(source, info, **args):
-        # type: (Optional[str], ResolveInfo, **Any) -> str
-        return json.dumps([source, args], separators=(",", ":"))
-
-    TestInputObject = GraphQLInputObjectType(
-        "TestInputObject",
-        lambda: OrderedDict(
-            [
-                (
-                    "inputOne",
-                    GraphQLInputObjectField(GraphQLString, out_name="input_one"),
-                ),
-                (
-                    "inputRecursive",
-                    GraphQLInputObjectField(
-                        TestInputObject, out_name="input_recursive"
-                    ),
-                ),
-            ]
-        ),
-    )
-
-    schema = _test_schema(
-        GraphQLField(
-            GraphQLString,
-            args=OrderedDict(
-                [("aInput", GraphQLArgument(TestInputObject, out_name="a_input"))]
-            ),
-            resolver=resolver,
-        )
-    )
-
-    result = graphql(schema, "{ test }", None)
-    assert not result.errors
-    assert result.data == {"test": "[null,{}]"}
-
-    result = graphql(schema, '{ test(aInput: {inputOne: "String!"} ) }', "Source!")
-    assert not result.errors
-    assert result.data == {"test": '["Source!",{"a_input":{"input_one":"String!"}}]'}
-
-    result = graphql(
-        schema,
-        '{ test(aInput: {inputRecursive:{inputOne: "SourceRecursive!"}} ) }',
-        "Source!",
-    )
-    assert not result.errors
-    assert result.data == {
-        "test": '["Source!",{"a_input":{"input_recursive":{"input_one":"SourceRecursive!"}}}]'
-    }
-
-
-def test_default_resolve_works_with_dicts():
-    schema = _test_schema(GraphQLField(GraphQLString))
-    result = graphql(schema, "{ test }", {"test": "testValue"})
-    assert not result.errors
-    assert result.data == {"test": "testValue"}
diff --git a/graphql/execution/tests/test_subscribe.py b/graphql/execution/tests/test_subscribe.py
deleted file mode 100644
index 2fa039c..0000000
--- a/graphql/execution/tests/test_subscribe.py
+++ /dev/null
@@ -1,427 +0,0 @@
-# type: ignore
-from collections import OrderedDict, namedtuple
-from rx import Observable, Observer
-from rx.subjects import Subject
-from graphql import (
-    parse,
-    GraphQLObjectType,
-    GraphQLString,
-    GraphQLBoolean,
-    GraphQLInt,
-    GraphQLField,
-    GraphQLList,
-    GraphQLSchema,
-    graphql,
-    subscribe,
-)
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from graphql.execution.base import ResolveInfo
-    from rx import Observable
-    from typing import Optional, Union, Any, Callable, Tuple
-    from graphql.execution.base import ExecutionResult
-
-Email = namedtuple("Email", "from_,subject,message,unread")
-
-EmailType = GraphQLObjectType(
-    name="Email",
-    fields=OrderedDict(
-        [
-            ("from", GraphQLField(GraphQLString, resolver=lambda x, info: x.from_)),
-            ("subject", GraphQLField(GraphQLString)),
-            ("message", GraphQLField(GraphQLString)),
-            ("unread", GraphQLField(GraphQLBoolean)),
-        ]
-    ),
-)
-
-InboxType = GraphQLObjectType(
-    name="Inbox",
-    fields=OrderedDict(
-        [
-            (
-                "total",
-                GraphQLField(
-                    GraphQLInt, resolver=lambda inbox, context: len(inbox.emails)
-                ),
-            ),
-            (
-                "unread",
-                GraphQLField(
-                    GraphQLInt,
-                    resolver=lambda inbox, context: len(
-                        [e for e in inbox.emails if e.unread]
-                    ),
-                ),
-            ),
-            ("emails", GraphQLField(GraphQLList(EmailType))),
-        ]
-    ),
-)
-
-QueryType = GraphQLObjectType(
-    name="Query", fields=OrderedDict([("inbox", GraphQLField(InboxType))])
-)
-
-EmailEventType = GraphQLObjectType(
-    name="EmailEvent",
-    fields=OrderedDict(
-        [
-            ("email", GraphQLField(EmailType, resolver=lambda root, info: root[0])),
-            ("inbox", GraphQLField(InboxType, resolver=lambda root, info: root[1])),
-        ]
-    ),
-)
-
-
-def get_unbound_function(func):
-    # type: (Callable) -> Callable
-    if not getattr(func, "__self__", True):
-        return func.__func__
-    return func
-
-
-def email_schema_with_resolvers(resolve_fn=None):
-    # type: (Callable) -> GraphQLSchema
-    def default_resolver(root, info):
-        # type: (Any, ResolveInfo) -> Union[Observable, Subject]
-        func = getattr(root, "importantEmail", None)
-        if func:
-            func = get_unbound_function(func)
-            return func()
-        return Observable.empty()
-
-    return GraphQLSchema(
-        query=QueryType,
-        subscription=GraphQLObjectType(
-            name="Subscription",
-            fields=OrderedDict(
-                [
-                    (
-                        "importantEmail",
-                        GraphQLField(
-                            EmailEventType, resolver=resolve_fn or default_resolver
-                        ),
-                    )
-                ]
-            ),
-        ),
-    )
-
-
-email_schema = email_schema_with_resolvers()
-
-
-class MyObserver(Observer):
-    def on_next(self, value):
-        self.has_on_next = value
-
-    def on_error(self, err):
-        self.has_on_error = err
-
-    def on_completed(self):
-        self.has_on_completed = True
-
-
-def create_subscription(
-    stream,  # type: Subject
-    schema=email_schema,  # type: GraphQLSchema
-    ast=None,  # type: Optional[Any]
-    vars=None,  # type: Optional[Any]
-):
-    # type: (...) -> Tuple[Callable, Union[ExecutionResult, Observable]]
-    class Root(object):
-        class inbox(object):
-            emails = [
-                Email(
-                    from_="joe@graphql.org",
-                    subject="Hello",
-                    message="Hello World",
-                    unread=False,
-                )
-            ]
-
-        def importantEmail():
-            return stream
-
-    def send_important_email(new_email):
-        # type: (Email) -> None
-        Root.inbox.emails.append(new_email)
-        stream.on_next((new_email, Root.inbox))
-        # stream.on_completed()
-
-    default_ast = parse(
-        """
-    subscription {
-      importantEmail {
-        email {
-          from
-          subject
-        }
-        inbox {
-          unread
-          total
-        }
-      }
-    }
-  """
-    )
-
-    return (
-        send_important_email,
-        graphql(schema, ast or default_ast, Root, None, vars, allow_subscriptions=True),
-    )
-
-
-def test_accepts_an_object_with_named_properties_as_arguments():
-    # type: () -> None
-    document = parse(
-        """
-      subscription {
-        importantEmail
-      }
-  """
-    )
-    result = subscribe(email_schema, document, root_value=None)
-    assert isinstance(result, Observable)
-
-
-def test_accepts_multiple_subscription_fields_defined_in_schema():
-    # type: () -> None
-    SubscriptionTypeMultiple = GraphQLObjectType(
-        name="Subscription",
-        fields=OrderedDict(
-            [
-                ("importantEmail", GraphQLField(EmailEventType)),
-                ("nonImportantEmail", GraphQLField(EmailEventType)),
-            ]
-        ),
-    )
-    test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionTypeMultiple)
-
-    stream = Subject()
-    send_important_email, subscription = create_subscription(stream, test_schema)
-
-    email = Email(
-        from_="yuzhi@graphql.org",
-        subject="Alright",
-        message="Tests are good",
-        unread=True,
-    )
-    l = []
-    stream.subscribe(l.append)
-    send_important_email(email)
-    assert l[0][0] == email
-
-
-def test_accepts_type_definition_with_sync_subscribe_function():
-    # type: () -> None
-    SubscriptionType = GraphQLObjectType(
-        name="Subscription",
-        fields=OrderedDict(
-            [
-                (
-                    "importantEmail",
-                    GraphQLField(
-                        EmailEventType, resolver=lambda *_: Observable.from_([None])
-                    ),
-                )
-            ]
-        ),
-    )
-    test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionType)
-
-    stream = Subject()
-    send_important_email, subscription = create_subscription(stream, test_schema)
-
-    email = Email(
-        from_="yuzhi@graphql.org",
-        subject="Alright",
-        message="Tests are good",
-        unread=True,
-    )
-    l = []
-    subscription.subscribe(l.append)
-    send_important_email(email)
-
-    assert l  # [0].data == {'importantEmail': None}
-
-
-def test_throws_an_error_if_subscribe_does_not_return_an_iterator():
-    # type: () -> None
-    SubscriptionType = GraphQLObjectType(
-        name="Subscription",
-        fields=OrderedDict(
-            [("importantEmail", GraphQLField(EmailEventType, resolver=lambda *_: None))]
-        ),
-    )
-    test_schema = GraphQLSchema(query=QueryType, subscription=SubscriptionType)
-
-    stream = Subject()
-    _, subscription = create_subscription(stream, test_schema)
-
-    assert (
-        str(subscription.errors[0])
-        == "Subscription must return Async Iterable or Observable. Received: None"
-    )
-
-
-def test_returns_an_error_if_subscribe_function_returns_error():
-    # type: () -> None
-    exc = Exception("Throw!")
-
-    def thrower(root, info):
-        # type: (Optional[Any], ResolveInfo) -> None
-        raise exc
-
-    erroring_email_schema = email_schema_with_resolvers(thrower)
-    result = subscribe(
-        erroring_email_schema,
-        parse(
-            """
-        subscription {
-          importantEmail
-        }
-    """
-        ),
-    )
-
-    assert result.errors == [exc]
-
-
-# Subscription Publish Phase
-def test_produces_a_payload_for_multiple_subscribe_in_same_subscription():
-    # type: () -> None
-    stream = Subject()
-    send_important_email, subscription1 = create_subscription(stream)
-    subscription2 = create_subscription(stream)[1]
-
-    payload1 = []
-    payload2 = []
-
-    subscription1.subscribe(payload1.append)
-    subscription2.subscribe(payload2.append)
-
-    email = Email(
-        from_="yuzhi@graphql.org",
-        subject="Alright",
-        message="Tests are good",
-        unread=True,
-    )
-
-    send_important_email(email)
-    expected_payload = {
-        "importantEmail": {
-            "email": {"from": "yuzhi@graphql.org", "subject": "Alright"},
-            "inbox": {"unread": 1, "total": 2},
-        }
-    }
-
-    assert payload1[0].data == expected_payload
-    assert payload2[0].data == expected_payload
-
-
-# Subscription Publish Phase
-def test_produces_a_payload_per_subscription_event():
-    # type: () -> None
-    stream = Subject()
-    send_important_email, subscription = create_subscription(stream)
-
-    payload = []
-
-    subscription.subscribe(payload.append)
-    send_important_email(
-        Email(
-            from_="yuzhi@graphql.org",
-            subject="Alright",
-            message="Tests are good",
-            unread=True,
-        )
-    )
-    expected_payload = {
-        "importantEmail": {
-            "email": {"from": "yuzhi@graphql.org", "subject": "Alright"},
-            "inbox": {"unread": 1, "total": 2},
-        }
-    }
-
-    assert len(payload) == 1
-    assert payload[0].data == expected_payload
-
-    send_important_email(
-        Email(
-            from_="hyo@graphql.org",
-            subject="Tools",
-            message="I <3 making things",
-            unread=True,
-        )
-    )
-    expected_payload = {
-        "importantEmail": {
-            "email": {"from": "hyo@graphql.org", "subject": "Tools"},
-            "inbox": {"unread": 2, "total": 3},
-        }
-    }
-
-    assert len(payload) == 2
-    assert payload[-1].data == expected_payload
-
-    # The client decides to disconnect
-    stream.on_completed()
-
-    send_important_email(
-        Email(
-            from_="adam@graphql.org",
-            subject="Important",
-            message="Read me please",
-            unread=True,
-        )
-    )
-
-    assert len(payload) == 2
-
-
-def test_event_order_is_correct_for_multiple_publishes():
-    # type: () -> None
-    stream = Subject()
-    send_important_email, subscription = create_subscription(stream)
-
-    payload = []
-
-    subscription.subscribe(payload.append)
-    send_important_email(
-        Email(
-            from_="yuzhi@graphql.org",
-            subject="Message",
-            message="Tests are good",
-            unread=True,
-        )
-    )
-    send_important_email(
-        Email(
-            from_="yuzhi@graphql.org",
-            subject="Message 2",
-            message="Tests are good 2",
-            unread=True,
-        )
-    )
-
-    expected_payload1 = {
-        "importantEmail": {
-            "email": {"from": "yuzhi@graphql.org", "subject": "Message"},
-            "inbox": {"unread": 1, "total": 2},
-        }
-    }
-
-    expected_payload2 = {
-        "importantEmail": {
-            "email": {"from": "yuzhi@graphql.org", "subject": "Message 2"},
-            "inbox": {"unread": 2, "total": 3},
-        }
-    }
-
-    assert len(payload) == 2
-    print(payload)
-    assert payload[0].data == expected_payload1
-    assert payload[1].data == expected_payload2
diff --git a/graphql/execution/tests/test_union_interface.py b/graphql/execution/tests/test_union_interface.py
deleted file mode 100644
index bd61ba3..0000000
--- a/graphql/execution/tests/test_union_interface.py
+++ /dev/null
@@ -1,376 +0,0 @@
-# type: ignore
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLBoolean,
-    GraphQLField,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-)
-
-# from typing import List
-# from graphql.execution.base import ResolveInfo
-# from graphql.type.definition import GraphQLObjectType
-# from typing import Union
-
-
-class Dog(object):
-    def __init__(self, name, barks):
-        self.name = name
-        self.barks = barks
-
-
-class Cat(object):
-    def __init__(self, name, meows):
-        self.name = name
-        self.meows = meows
-
-
-class Person(object):
-    def __init__(self, name, pets, friends):
-        # type: (str, List, List[Person]) -> None
-        self.name = name
-        self.pets = pets
-        self.friends = friends
-
-
-NamedType = GraphQLInterfaceType("Named", {"name": GraphQLField(GraphQLString)})
-
-DogType = GraphQLObjectType(
-    name="Dog",
-    interfaces=[NamedType],
-    fields={"name": GraphQLField(GraphQLString), "barks": GraphQLField(GraphQLBoolean)},
-    is_type_of=lambda value, info: isinstance(value, Dog),
-)
-
-CatType = GraphQLObjectType(
-    name="Cat",
-    interfaces=[NamedType],
-    fields={"name": GraphQLField(GraphQLString), "meows": GraphQLField(GraphQLBoolean)},
-    is_type_of=lambda value, info: isinstance(value, Cat),
-)
-
-
-def resolve_pet_type(value, info):
-    # type: (Union[Cat, Dog], ResolveInfo) -> GraphQLObjectType
-    if isinstance(value, Dog):
-        return DogType
-    if isinstance(value, Cat):
-        return CatType
-
-
-PetType = GraphQLUnionType("Pet", [DogType, CatType], resolve_type=resolve_pet_type)
-
-PersonType = GraphQLObjectType(
-    name="Person",
-    interfaces=[NamedType],
-    fields={
-        "name": GraphQLField(GraphQLString),
-        "pets": GraphQLField(GraphQLList(PetType)),
-        "friends": GraphQLField(GraphQLList(NamedType)),
-    },
-    is_type_of=lambda value, info: isinstance(value, Person),
-)
-
-schema = GraphQLSchema(query=PersonType, types=[PetType])
-
-garfield = Cat("Garfield", False)
-odie = Dog("Odie", True)
-liz = Person("Liz", [], [])
-john = Person("John", [garfield, odie], [liz, odie])
-
-
-# Execute: Union and intersection types
-
-
-def test_can_introspect_on_union_and_intersection_types():
-    # type: () -> None
-    ast = parse(
-        """
-    {
-        Named: __type(name: "Named") {
-            kind
-            name
-            fields { name }
-            interfaces { name }
-            possibleTypes { name }
-            enumValues { name }
-            inputFields { name }
-        }
-        Pet: __type(name: "Pet") {
-            kind
-            name
-            fields { name }
-            interfaces { name }
-            possibleTypes { name }
-            enumValues { name }
-            inputFields { name }
-        }
-    }"""
-    )
-
-    result = execute(schema, ast)
-    assert not result.errors
-    assert result.data == {
-        "Named": {
-            "enumValues": None,
-            "name": "Named",
-            "kind": "INTERFACE",
-            "interfaces": None,
-            "fields": [{"name": "name"}],
-            "possibleTypes": [{"name": "Person"}, {"name": "Dog"}, {"name": "Cat"}],
-            "inputFields": None,
-        },
-        "Pet": {
-            "enumValues": None,
-            "name": "Pet",
-            "kind": "UNION",
-            "interfaces": None,
-            "fields": None,
-            "possibleTypes": [{"name": "Dog"}, {"name": "Cat"}],
-            "inputFields": None,
-        },
-    }
-
-
-def test_executes_using_union_types():
-    # type: () -> None
-    # NOTE: This is an *invalid* query, but it should be an *executable* query.
-    ast = parse(
-        """
-        {
-            __typename
-            name
-            pets {
-                __typename
-                name
-                barks
-                meows
-            }
-        }
-    """
-    )
-    result = execute(schema, ast, john)
-    assert not result.errors
-    assert result.data == {
-        "__typename": "Person",
-        "name": "John",
-        "pets": [
-            {"__typename": "Cat", "name": "Garfield", "meows": False},
-            {"__typename": "Dog", "name": "Odie", "barks": True},
-        ],
-    }
-
-
-def test_executes_union_types_with_inline_fragment():
-    # type: () -> None
-    # This is the valid version of the query in the above test.
-    ast = parse(
-        """
-      {
-        __typename
-        name
-        pets {
-          __typename
-          ... on Dog {
-            name
-            barks
-          }
-          ... on Cat {
-            name
-            meows
-          }
-        }
-      }
-    """
-    )
-    result = execute(schema, ast, john)
-    assert not result.errors
-    assert result.data == {
-        "__typename": "Person",
-        "name": "John",
-        "pets": [
-            {"__typename": "Cat", "name": "Garfield", "meows": False},
-            {"__typename": "Dog", "name": "Odie", "barks": True},
-        ],
-    }
-
-
-def test_executes_using_interface_types():
-    # type: () -> None
-    # NOTE: This is an *invalid* query, but it should be an *executable* query.
-    ast = parse(
-        """
-      {
-        __typename
-        name
-        friends {
-          __typename
-          name
-          barks
-          meows
-        }
-      }
-    """
-    )
-    result = execute(schema, ast, john)
-    assert not result.errors
-    assert result.data == {
-        "__typename": "Person",
-        "name": "John",
-        "friends": [
-            {"__typename": "Person", "name": "Liz"},
-            {"__typename": "Dog", "name": "Odie", "barks": True},
-        ],
-    }
-
-
-def test_executes_interface_types_with_inline_fragment():
-    # type: () -> None
-    # This is the valid version of the query in the above test.
-    ast = parse(
-        """
-      {
-        __typename
-        name
-        friends {
-          __typename
-          name
-          ... on Dog {
-            barks
-          }
-          ... on Cat {
-            meows
-          }
-        }
-      }
-    """
-    )
-    result = execute(schema, ast, john)
-    assert not result.errors
-    assert result.data == {
-        "__typename": "Person",
-        "name": "John",
-        "friends": [
-            {"__typename": "Person", "name": "Liz"},
-            {"__typename": "Dog", "name": "Odie", "barks": True},
-        ],
-    }
-
-
-def test_allows_fragment_conditions_to_be_abstract_types():
-    # type: () -> None
-    ast = parse(
-        """
-      {
-        __typename
-        name
-        pets { ...PetFields }
-        friends { ...FriendFields }
-      }
-      fragment PetFields on Pet {
-        __typename
-        ... on Dog {
-          name
-          barks
-        }
-        ... on Cat {
-          name
-          meows
-        }
-      }
-      fragment FriendFields on Named {
-        __typename
-        name
-        ... on Dog {
-          barks
-        }
-        ... on Cat {
-          meows
-        }
-      }
-    """
-    )
-    result = execute(schema, ast, john)
-    assert not result.errors
-    assert result.data == {
-        "__typename": "Person",
-        "name": "John",
-        "pets": [
-            {"__typename": "Cat", "name": "Garfield", "meows": False},
-            {"__typename": "Dog", "name": "Odie", "barks": True},
-        ],
-        "friends": [
-            {"__typename": "Person", "name": "Liz"},
-            {"__typename": "Dog", "name": "Odie", "barks": True},
-        ],
-    }
-
-
-def test_only_include_fields_from_matching_fragment_condition():
-    # type: () -> None
-    ast = parse(
-        """
-      {
-        pets { ...PetFields }
-      }
-      fragment PetFields on Pet {
-        __typename
-        ... on Dog {
-          name
-        }
-      }
-    """
-    )
-    result = execute(schema, ast, john)
-    assert not result.errors
-    assert result.data == {
-        "pets": [{"__typename": "Cat"}, {"__typename": "Dog", "name": "Odie"}]
-    }
-
-
-def test_gets_execution_info_in_resolver():
-    # type: () -> None
-    class encountered:
-        schema = None
-        root_value = None
-        context = None
-
-    def resolve_type(obj, info):
-        # type: (Person, ResolveInfo) -> GraphQLObjectType
-        encountered.schema = info.schema
-        encountered.root_value = info.root_value
-        encountered.context = context
-        return PersonType2
-
-    NamedType2 = GraphQLInterfaceType(
-        name="Named",
-        fields={"name": GraphQLField(GraphQLString)},
-        resolve_type=resolve_type,
-    )
-
-    PersonType2 = GraphQLObjectType(
-        name="Person",
-        interfaces=[NamedType2],
-        fields={
-            "name": GraphQLField(GraphQLString),
-            "friends": GraphQLField(GraphQLList(NamedType2)),
-        },
-    )
-
-    schema2 = GraphQLSchema(query=PersonType2)
-    john2 = Person("John", [], [liz])
-    context = {"hey"}
-    ast = parse("""{ name, friends { name } }""")
-
-    result = execute(schema2, ast, john2, context_value=context)
-    assert not result.errors
-    assert result.data == {"name": "John", "friends": [{"name": "Liz"}]}
-
-    assert encountered.schema == schema2
-    assert encountered.root_value == john2
-    assert encountered.context == context
diff --git a/graphql/execution/tests/test_variables.py b/graphql/execution/tests/test_variables.py
deleted file mode 100644
index a4a1a8f..0000000
--- a/graphql/execution/tests/test_variables.py
+++ /dev/null
@@ -1,783 +0,0 @@
-# type: ignore
-import json
-from collections import OrderedDict
-
-from pytest import raises
-from graphql.error import GraphQLError, format_error
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLField,
-    GraphQLBoolean,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLSchema,
-    GraphQLString,
-)
-
-# from typing import Any
-# from graphql.execution.base import ResolveInfo
-# from typing import Optional
-# from typing import Dict
-# from typing import Union
-
-TestComplexScalar = GraphQLScalarType(
-    name="ComplexScalar",
-    serialize=lambda v: "SerializedValue" if v == "DeserializedValue" else None,
-    parse_value=lambda v: "DeserializedValue" if v == "SerializedValue" else None,
-    parse_literal=lambda v: "DeserializedValue"
-    if v.value == "SerializedValue"
-    else None,
-)
-
-
-class my_special_dict(dict):
-    pass
-
-
-TestInputObject = GraphQLInputObjectType(
-    "TestInputObject",
-    OrderedDict(
-        [
-            ("a", GraphQLInputObjectField(GraphQLString)),
-            ("b", GraphQLInputObjectField(GraphQLList(GraphQLString))),
-            ("c", GraphQLInputObjectField(GraphQLNonNull(GraphQLString))),
-            ("d", GraphQLInputObjectField(TestComplexScalar)),
-        ]
-    ),
-)
-
-
-TestCustomInputObject = GraphQLInputObjectType(
-    "TestCustomInputObject",
-    OrderedDict([("a", GraphQLInputObjectField(GraphQLString))]),
-    container_type=my_special_dict,
-)
-
-
-def stringify(obj):
-    # type: (Any) -> str
-    return json.dumps(obj, sort_keys=True)
-
-
-def input_to_json(obj, info, **args):
-    # type: (Optional[Any], ResolveInfo, **Any) -> Optional[str]
-    input = args.get("input")
-    if input:
-        return stringify(input)
-
-
-TestNestedInputObject = GraphQLInputObjectType(
-    name="TestNestedInputObject",
-    fields={
-        "na": GraphQLInputObjectField(GraphQLNonNull(TestInputObject)),
-        "nb": GraphQLInputObjectField(GraphQLNonNull(GraphQLString)),
-    },
-)
-
-TestType = GraphQLObjectType(
-    "TestType",
-    {
-        "fieldWithObjectInput": GraphQLField(
-            GraphQLString,
-            args={"input": GraphQLArgument(TestInputObject)},
-            resolver=input_to_json,
-        ),
-        "fieldWithCustomObjectInput": GraphQLField(
-            GraphQLBoolean,
-            args={"input": GraphQLArgument(TestCustomInputObject)},
-            resolver=lambda root, info, **args: isinstance(
-                args.get("input"), my_special_dict
-            ),
-        ),
-        "fieldWithNullableStringInput": GraphQLField(
-            GraphQLString,
-            args={"input": GraphQLArgument(GraphQLString)},
-            resolver=input_to_json,
-        ),
-        "fieldWithNonNullableStringInput": GraphQLField(
-            GraphQLString,
-            args={"input": GraphQLArgument(GraphQLNonNull(GraphQLString))},
-            resolver=input_to_json,
-        ),
-        "fieldWithDefaultArgumentValue": GraphQLField(
-            GraphQLString,
-            args={"input": GraphQLArgument(GraphQLString, "Hello World")},
-            resolver=input_to_json,
-        ),
-        "fieldWithNestedInputObject": GraphQLField(
-            GraphQLString,
-            args={"input": GraphQLArgument(TestNestedInputObject, "Hello World")},
-            resolver=input_to_json,
-        ),
-        "list": GraphQLField(
-            GraphQLString,
-            args={"input": GraphQLArgument(GraphQLList(GraphQLString))},
-            resolver=input_to_json,
-        ),
-        "nnList": GraphQLField(
-            GraphQLString,
-            args={"input": GraphQLArgument(GraphQLNonNull(GraphQLList(GraphQLString)))},
-            resolver=input_to_json,
-        ),
-        "listNN": GraphQLField(
-            GraphQLString,
-            args={"input": GraphQLArgument(GraphQLList(GraphQLNonNull(GraphQLString)))},
-            resolver=input_to_json,
-        ),
-        "nnListNN": GraphQLField(
-            GraphQLString,
-            args={
-                "input": GraphQLArgument(
-                    GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString)))
-                )
-            },
-            resolver=input_to_json,
-        ),
-    },
-)
-
-schema = GraphQLSchema(TestType)
-
-
-def check(
-    doc,  # type: str
-    expected,  # type: Union[Dict[str, Dict[str, None]], Dict[str, Dict[str, str]]]
-    args=None,  # type: Any
-):
-    # type: (...) -> None
-    ast = parse(doc)
-    response = execute(schema, ast, variable_values=args)
-
-    if response.errors:
-        result = {
-            "data": response.data,
-            "errors": [format_error(e) for e in response.errors],
-        }
-    else:
-        result = {"data": response.data}
-
-    assert result == expected
-
-
-# Handles objects and nullability
-
-
-def test_inline_executes_with_complex_input():
-    # type: () -> None
-    doc = """
-    {
-      fieldWithObjectInput(input: {a: "foo", b: ["bar"], c: "baz"})
-    }
-    """
-    check(
-        doc,
-        {
-            "data": {
-                "fieldWithObjectInput": stringify(
-                    {"a": "foo", "b": ["bar"], "c": "baz"}
-                )
-            }
-        },
-    )
-
-
-def test_properly_parses_single_value_to_list():
-    # type: () -> None
-    doc = """
-    {
-        fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"})
-    }
-    """
-    check(
-        doc,
-        {
-            "data": {
-                "fieldWithObjectInput": stringify(
-                    {"a": "foo", "b": ["bar"], "c": "baz"}
-                )
-            }
-        },
-    )
-
-
-def test_does_not_use_incorrect_value():
-    # type: () -> None
-    doc = """
-    {
-        fieldWithObjectInput(input: ["foo", "bar", "baz"])
-    }
-    """
-    check(doc, {"data": {"fieldWithObjectInput": None}})
-
-
-def test_properly_runs_parse_literal_on_complex_scalar_types():
-    # type: () -> None
-    doc = """
-    {
-        fieldWithObjectInput(input: {a: "foo", d: "SerializedValue"})
-    }
-    """
-    check(
-        doc,
-        {"data": {"fieldWithObjectInput": '{"a": "foo", "d": "DeserializedValue"}'}},
-    )
-
-
-# noinspection PyMethodMayBeStatic
-class TestUsingVariables:
-    doc = """
-    query q($input: TestInputObject) {
-      fieldWithObjectInput(input: $input)
-    }
-    """
-
-    def test_executes_with_complex_input(self):
-        # type: () -> None
-        params = {"input": {"a": "foo", "b": ["bar"], "c": "baz"}}
-        check(
-            self.doc,
-            {
-                "data": {
-                    "fieldWithObjectInput": stringify(
-                        {"a": "foo", "b": ["bar"], "c": "baz"}
-                    )
-                }
-            },
-            params,
-        )
-
-    def test_uses_default_value_when_not_provided(self):
-        # type: () -> None
-        with_defaults_doc = """
-        query q($input: TestInputObject = {a: "foo", b: ["bar"], c: "baz"}) {
-            fieldWithObjectInput(input: $input)
-        }
-        """
-
-        check(
-            with_defaults_doc,
-            {
-                "data": {
-                    "fieldWithObjectInput": stringify(
-                        {"a": "foo", "b": ["bar"], "c": "baz"}
-                    )
-                }
-            },
-        )
-
-    def test_properly_parses_single_value_to_list(self):
-        # type: () -> None
-        params = {"input": {"a": "foo", "b": "bar", "c": "baz"}}
-        check(
-            self.doc,
-            {
-                "data": {
-                    "fieldWithObjectInput": stringify(
-                        {"a": "foo", "b": ["bar"], "c": "baz"}
-                    )
-                }
-            },
-            params,
-        )
-
-    def test_executes_with_complex_scalar_input(self):
-        # type: () -> None
-        params = {"input": {"c": "foo", "d": "SerializedValue"}}
-        check(
-            self.doc,
-            {
-                "data": {
-                    "fieldWithObjectInput": stringify(
-                        {"c": "foo", "d": "DeserializedValue"}
-                    )
-                }
-            },
-            params,
-        )
-
-    def test_errors_on_null_for_nested_non_null(self):
-        # type: () -> None
-        params = {"input": {"a": "foo", "b": "bar", "c": None}}
-
-        with raises(GraphQLError) as excinfo:
-            check(self.doc, {}, params)
-
-        assert format_error(excinfo.value) == {
-            "locations": [{"column": 13, "line": 2}],
-            "message": 'Variable "$input" got invalid value {}.\n'
-            'In field "c": Expected "String!", found null.'.format(
-                stringify(params["input"])
-            ),
-        }
-
-    def test_errors_on_incorrect_type(self):
-        # type: () -> None
-        params = {"input": "foo bar"}
-
-        with raises(GraphQLError) as excinfo:
-            check(self.doc, {}, params)
-
-        assert format_error(excinfo.value) == {
-            "locations": [{"column": 13, "line": 2}],
-            "message": 'Variable "$input" got invalid value {}.\n'
-            'Expected "TestInputObject", found not an object.'.format(
-                stringify(params["input"])
-            ),
-        }
-
-    def test_errors_on_omission_of_nested_non_null(self):
-        # type: () -> None
-        params = {"input": {"a": "foo", "b": "bar"}}
-
-        with raises(GraphQLError) as excinfo:
-            check(self.doc, {}, params)
-
-        assert format_error(excinfo.value) == {
-            "locations": [{"column": 13, "line": 2}],
-            "message": 'Variable "$input" got invalid value {}.\n'
-            'In field "c": Expected "String!", found null.'.format(
-                stringify(params["input"])
-            ),
-        }
-
-    def test_errors_on_deep_nested_errors_and_with_many_errors(self):
-        # type: () -> None
-        nested_doc = """
-          query q($input: TestNestedInputObject) {
-            fieldWithNestedObjectInput(input: $input)
-          }
-        """
-
-        params = {"input": {"na": {"a": "foo"}}}
-        with raises(GraphQLError) as excinfo:
-            check(nested_doc, {}, params)
-
-        assert format_error(excinfo.value) == {
-            "locations": [{"column": 19, "line": 2}],
-            "message": 'Variable "$input" got invalid value {}.\n'
-            'In field "na": In field "c": Expected "String!", found null.\n'
-            'In field "nb": Expected "String!", found null.'.format(
-                stringify(params["input"])
-            ),
-        }
-
-    def test_errors_on_addition_of_input_field_of_incorrect_type(self):
-        # type: () -> None
-        params = {"input": {"a": "foo", "b": "bar", "c": "baz", "d": "dog"}}
-
-        with raises(GraphQLError) as excinfo:
-            check(self.doc, {}, params)
-
-        assert format_error(excinfo.value) == {
-            "locations": [{"column": 13, "line": 2}],
-            "message": 'Variable "$input" got invalid value {}.\n'
-            'In field "d": Expected type "ComplexScalar", found "dog".'.format(
-                stringify(params["input"])
-            ),
-        }
-
-    def test_errors_on_addition_of_unknown_input_field(self):
-        # type: () -> None
-        params = {"input": {"a": "foo", "b": "bar", "c": "baz", "extra": "dog"}}
-
-        with raises(GraphQLError) as excinfo:
-            check(self.doc, {}, params)
-
-        assert format_error(excinfo.value) == {
-            "locations": [{"column": 13, "line": 2}],
-            "message": 'Variable "$input" got invalid value {}.\n'
-            'In field "extra": Unknown field.'.format(stringify(params["input"])),
-        }
-
-
-def test_allows_nullable_inputs_to_be_omitted():
-    # type: () -> None
-    doc = "{ fieldWithNullableStringInput }"
-    check(doc, {"data": {"fieldWithNullableStringInput": None}})
-
-
-def test_allows_nullable_inputs_to_be_omitted_in_a_variable():
-    # type: () -> None
-    doc = """
-    query SetsNullable($value: String) {
-        fieldWithNullableStringInput(input: $value)
-    }
-    """
-
-    check(doc, {"data": {"fieldWithNullableStringInput": None}})
-
-
-def test_allows_nullable_inputs_to_be_omitted_in_an_unlisted_variable():
-    # type: () -> None
-    doc = """
-    query SetsNullable {
-        fieldWithNullableStringInput(input: $value)
-    }
-    """
-
-    check(doc, {"data": {"fieldWithNullableStringInput": None}})
-
-
-def test_allows_nullable_inputs_to_be_set_to_null_in_a_variable():
-    # type: () -> None
-    doc = """
-    query SetsNullable($value: String) {
-        fieldWithNullableStringInput(input: $value)
-    }
-    """
-    check(doc, {"data": {"fieldWithNullableStringInput": None}}, {"value": None})
-
-
-def test_allows_nullable_inputs_to_be_set_to_a_value_in_a_variable():
-    # type: () -> None
-    doc = """
-    query SetsNullable($value: String) {
-        fieldWithNullableStringInput(input: $value)
-    }
-    """
-
-    check(doc, {"data": {"fieldWithNullableStringInput": '"a"'}}, {"value": "a"})
-
-
-def test_allows_nullable_inputs_to_be_set_to_a_value_directly():
-    # type: () -> None
-    doc = """
-    {
-        fieldWithNullableStringInput(input: "a")
-    }
-    """
-    check(doc, {"data": {"fieldWithNullableStringInput": '"a"'}})
-
-
-def test_does_not_allow_non_nullable_inputs_to_be_omitted_in_a_variable():
-    # type: () -> None
-    doc = """
-    query SetsNonNullable($value: String!) {
-        fieldWithNonNullableStringInput(input: $value)
-    }
-    """
-    with raises(GraphQLError) as excinfo:
-        check(doc, {})
-
-    assert format_error(excinfo.value) == {
-        "locations": [{"column": 27, "line": 2}],
-        "message": 'Variable "$value" of required type "String!" was not provided.',
-    }
-
-
-def test_does_not_allow_non_nullable_inputs_to_be_set_to_null_in_a_variable():
-    # type: () -> None
-    doc = """
-    query SetsNonNullable($value: String!) {
-        fieldWithNonNullableStringInput(input: $value)
-    }
-    """
-
-    with raises(GraphQLError) as excinfo:
-        check(doc, {}, {"value": None})
-
-    assert format_error(excinfo.value) == {
-        "locations": [{"column": 27, "line": 2}],
-        "message": 'Variable "$value" of required type "String!" was not provided.',
-    }
-
-
-def test_allows_non_nullable_inputs_to_be_set_to_a_value_in_a_variable():
-    # type: () -> None
-    doc = """
-    query SetsNonNullable($value: String!) {
-        fieldWithNonNullableStringInput(input: $value)
-    }
-    """
-
-    check(doc, {"data": {"fieldWithNonNullableStringInput": '"a"'}}, {"value": "a"})
-
-
-def test_allows_non_nullable_inputs_to_be_set_to_a_value_directly():
-    # type: () -> None
-    doc = """
-    {
-        fieldWithNonNullableStringInput(input: "a")
-    }
-    """
-
-    check(doc, {"data": {"fieldWithNonNullableStringInput": '"a"'}})
-
-
-def test_passes_along_null_for_non_nullable_inputs_if_explcitly_set_in_the_query():
-    # type: () -> None
-    doc = """
-    {
-        fieldWithNonNullableStringInput
-    }
-    """
-
-    check(
-        doc,
-        {
-            "errors": [
-                {
-                    "message": 'Argument "input" of required type String!" was not provided.'
-                }
-            ],
-            "data": None,
-        },
-    )
-
-
-def test_uses_objectinput_container():
-    # type: () -> None
-    doc = """
-    {
-        fieldWithCustomObjectInput(input: {a: "b"})
-    }
-    """
-
-    check(doc, {"data": {"fieldWithCustomObjectInput": True}})
-
-
-def test_allows_lists_to_be_null():
-    # type: () -> None
-    doc = """
-    query q($input: [String]) {
-        list(input: $input)
-    }
-    """
-
-    check(doc, {"data": {"list": None}})
-
-
-def test_allows_lists_to_contain_values():
-    # type: () -> None
-    doc = """
-    query q($input: [String]) {
-        list(input: $input)
-    }
-    """
-
-    check(doc, {"data": {"list": stringify(["A"])}}, {"input": ["A"]})
-
-
-def test_allows_lists_to_contain_null():
-    # type: () -> None
-    doc = """
-    query q($input: [String]) {
-        list(input: $input)
-    }
-    """
-
-    check(
-        doc,
-        {"data": {"list": stringify(["A", None, "B"])}},
-        {"input": ["A", None, "B"]},
-    )
-
-
-def test_does_not_allow_non_null_lists_to_be_null():
-    # type: () -> None
-    doc = """
-    query q($input: [String]!) {
-        nnList(input: $input)
-    }
-    """
-
-    with raises(GraphQLError) as excinfo:
-        check(doc, {}, {"input": None})
-
-    assert format_error(excinfo.value) == {
-        "locations": [{"column": 13, "line": 2}],
-        "message": 'Variable "$input" of required type "[String]!" was not provided.',
-    }
-
-
-def test_allows_non_null_lists_to_contain_values():
-    # type: () -> None
-    doc = """
-    query q($input: [String]!) {
-        nnList(input: $input)
-    }
-    """
-
-    check(doc, {"data": {"nnList": stringify(["A"])}}, {"input": ["A"]})
-
-
-def test_allows_non_null_lists_to_contain_null():
-    # type: () -> None
-    doc = """
-    query q($input: [String]!) {
-        nnList(input: $input)
-    }
-    """
-
-    check(
-        doc,
-        {"data": {"nnList": stringify(["A", None, "B"])}},
-        {"input": ["A", None, "B"]},
-    )
-
-
-def test_allows_lists_of_non_nulls_to_be_null():
-    # type: () -> None
-    doc = """
-    query q($input: [String!]) {
-        listNN(input: $input)
-    }
-    """
-
-    check(doc, {"data": {"listNN": None}}, {"input": None})
-
-
-def test_allows_lists_of_non_nulls_to_contain_values():
-    # type: () -> None
-    doc = """
-    query q($input: [String!]) {
-        listNN(input: $input)
-    }
-    """
-
-    check(doc, {"data": {"listNN": stringify(["A"])}}, {"input": ["A"]})
-
-
-def test_does_not_allow_lists_of_non_nulls_to_contain_null():
-    # type: () -> None
-    doc = """
-    query q($input: [String!]) {
-        listNN(input: $input)
-    }
-    """
-
-    params = {"input": ["A", None, "B"]}
-
-    with raises(GraphQLError) as excinfo:
-        check(doc, {}, params)
-
-    assert format_error(excinfo.value) == {
-        "locations": [{"column": 13, "line": 2}],
-        "message": 'Variable "$input" got invalid value {}.\n'
-        'In element #1: Expected "String!", found null.'.format(
-            stringify(params["input"])
-        ),
-    }
-
-
-def test_does_not_allow_non_null_lists_of_non_nulls_to_be_null():
-    # type: () -> None
-    doc = """
-    query q($input: [String!]!) {
-        nnListNN(input: $input)
-    }
-    """
-    with raises(GraphQLError) as excinfo:
-        check(doc, {}, {"input": None})
-
-    assert format_error(excinfo.value) == {
-        "locations": [{"column": 13, "line": 2}],
-        "message": 'Variable "$input" of required type "[String!]!" was not provided.',
-    }
-
-
-def test_allows_non_null_lists_of_non_nulls_to_contain_values():
-    # type: () -> None
-    doc = """
-    query q($input: [String!]!) {
-        nnListNN(input: $input)
-    }
-    """
-
-    check(doc, {"data": {"nnListNN": stringify(["A"])}}, {"input": ["A"]})
-
-
-def test_does_not_allow_non_null_lists_of_non_nulls_to_contain_null():
-    # type: () -> None
-    doc = """
-    query q($input: [String!]!) {
-        nnListNN(input: $input)
-    }
-    """
-
-    params = {"input": ["A", None, "B"]}
-
-    with raises(GraphQLError) as excinfo:
-        check(doc, {}, params)
-
-    assert format_error(excinfo.value) == {
-        "locations": [{"column": 13, "line": 2}],
-        "message": 'Variable "$input" got invalid value {}.\n'
-        'In element #1: Expected "String!", found null.'.format(
-            stringify(params["input"])
-        ),
-    }
-
-
-def test_does_not_allow_invalid_types_to_be_used_as_values():
-    # type: () -> None
-    doc = """
-    query q($input: TestType!) {
-        fieldWithObjectInput(input: $input)
-    }
-    """
-    params = {"input": {"list": ["A", "B"]}}
-
-    with raises(GraphQLError) as excinfo:
-        check(doc, {}, params)
-
-    assert format_error(excinfo.value) == {
-        "locations": [{"column": 13, "line": 2}],
-        "message": 'Variable "$input" expected value of type "TestType!" which cannot be used as an input type.',
-    }
-
-
-def test_does_not_allow_unknown_types_to_be_used_as_values():
-    # type: () -> None
-    doc = """
-    query q($input: UnknownType!) {
-        fieldWithObjectInput(input: $input)
-    }
-    """
-    params = {"input": "whoknows"}
-
-    with raises(GraphQLError) as excinfo:
-        check(doc, {}, params)
-
-    assert format_error(excinfo.value) == {
-        "locations": [{"column": 13, "line": 2}],
-        "message": 'Variable "$input" expected value of type "UnknownType!" which cannot be used as an input type.',
-    }
-
-
-# noinspection PyMethodMayBeStatic
-class TestUsesArgumentDefaultValues:
-    def test_when_no_argument_provided(self):
-        # type: () -> None
-        check(
-            "{ fieldWithDefaultArgumentValue }",
-            {"data": {"fieldWithDefaultArgumentValue": '"Hello World"'}},
-        )
-
-    def test_when_nullable_variable_provided(self):
-        # type: () -> None
-        check(
-            """
-        query optionalVariable($optional: String) {
-            fieldWithDefaultArgumentValue(input: $optional)
-        }
-        """,
-            {"data": {"fieldWithDefaultArgumentValue": '"Hello World"'}},
-        )
-
-    def test_when_argument_provided_cannot_be_parsed(self):
-        # type: () -> None
-        check(
-            """
-        {
-            fieldWithDefaultArgumentValue(input: WRONG_TYPE)
-        }
-        """,
-            {"data": {"fieldWithDefaultArgumentValue": '"Hello World"'}},
-        )
diff --git a/graphql/execution/tests/utils.py b/graphql/execution/tests/utils.py
deleted file mode 100644
index afb44fa..0000000
--- a/graphql/execution/tests/utils.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from promise import Promise
-from typing import Any
-
-
-def resolved(value):
-    # type: (Any) -> Promise
-    return Promise.fulfilled(value)
-
-
-def rejected(error):
-    # type: (Exception) -> Promise
-    return Promise.rejected(error)
diff --git a/graphql/execution/utils.py b/graphql/execution/utils.py
deleted file mode 100644
index e1aab18..0000000
--- a/graphql/execution/utils.py
+++ /dev/null
@@ -1,384 +0,0 @@
-# -*- coding: utf-8 -*-
-import logging
-from traceback import format_exception
-
-from ..error import GraphQLError
-from ..language import ast
-from ..pyutils.default_ordered_dict import DefaultOrderedDict
-from ..type.definition import GraphQLInterfaceType, GraphQLUnionType
-from ..type.directives import GraphQLIncludeDirective, GraphQLSkipDirective
-from ..type.introspection import (
-    SchemaMetaFieldDef,
-    TypeMetaFieldDef,
-    TypeNameMetaFieldDef,
-)
-from ..utils.type_from_ast import type_from_ast
-from .values import get_argument_values, get_variable_values
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..type.definition import GraphQLObjectType, GraphQLField
-    from ..type.schema import GraphQLSchema
-    from ..language.ast import (
-        Document,
-        OperationDefinition,
-        SelectionSet,
-        Directive,
-        FragmentDefinition,
-        InlineFragment,
-        Field,
-    )
-    from .base import ResolveInfo
-    from types import TracebackType
-    from typing import Any, List, Dict, Optional, Union, Callable, Set, Tuple
-
-logger = logging.getLogger(__name__)
-
-
-class ExecutionContext(object):
-    """Data that must be available at all points during query execution.
-
-    Namely, schema of the type system that is currently executing,
-    and the fragments defined in the query document"""
-
-    __slots__ = (
-        "schema",
-        "fragments",
-        "root_value",
-        "operation",
-        "variable_values",
-        "errors",
-        "context_value",
-        "argument_values_cache",
-        "executor",
-        "middleware",
-        "allow_subscriptions",
-        "_subfields_cache",
-    )
-
-    def __init__(
-        self,
-        schema,  # type: GraphQLSchema
-        document_ast,  # type: Document
-        root_value,  # type: Any
-        context_value,  # type: Any
-        variable_values,  # type: Optional[Dict[str, Any]]
-        operation_name,  # type: Optional[str]
-        executor,  # type: Any
-        middleware,  # type: Optional[Any]
-        allow_subscriptions,  # type: bool
-    ):
-        # type: (...) -> None
-        """Constructs a ExecutionContext object from the arguments passed
-        to execute, which we will pass throughout the other execution
-        methods."""
-        errors = []  # type: List[Exception]
-        operation = None
-        fragments = {}  # type: Dict[str, FragmentDefinition]
-
-        for definition in document_ast.definitions:
-            if isinstance(definition, ast.OperationDefinition):
-                if not operation_name and operation:
-                    raise GraphQLError(
-                        "Must provide operation name if query contains multiple operations."
-                    )
-
-                if (
-                    not operation_name
-                    or definition.name
-                    and definition.name.value == operation_name
-                ):
-                    operation = definition
-
-            elif isinstance(definition, ast.FragmentDefinition):
-                fragments[definition.name.value] = definition
-
-            else:
-                raise GraphQLError(
-                    u"GraphQL cannot execute a request containing a {}.".format(
-                        definition.__class__.__name__
-                    ),
-                    definition,
-                )
-
-        if not operation:
-            if operation_name:
-                raise GraphQLError(
-                    u'Unknown operation named "{}".'.format(operation_name)
-                )
-
-            else:
-                raise GraphQLError("Must provide an operation.")
-
-        variable_values = get_variable_values(
-            schema, operation.variable_definitions or [], variable_values
-        )
-
-        self.schema = schema
-        self.fragments = fragments
-        self.root_value = root_value
-        self.operation = operation
-        self.variable_values = variable_values
-        self.errors = errors
-        self.context_value = context_value
-        self.argument_values_cache = (
-            {}
-        )  # type: Dict[Tuple[GraphQLField, Field], Dict[str, Any]]
-        self.executor = executor
-        self.middleware = middleware
-        self.allow_subscriptions = allow_subscriptions
-        self._subfields_cache = (
-            {}
-        )  # type: Dict[Tuple[GraphQLObjectType, Tuple[Field, ...]], DefaultOrderedDict]
-
-    def get_field_resolver(self, field_resolver):
-        # type: (Callable) -> Callable
-        if not self.middleware:
-            return field_resolver
-        return self.middleware.get_field_resolver(field_resolver)
-
-    def get_argument_values(self, field_def, field_ast):
-        # type: (GraphQLField, Field) -> Dict[str, Any]
-        k = field_def, field_ast
-        if k not in self.argument_values_cache:
-            self.argument_values_cache[k] = get_argument_values(
-                field_def.args, field_ast.arguments, self.variable_values
-            )
-
-        return self.argument_values_cache[k]
-
-    def report_error(self, error, traceback=None):
-        # type: (Exception, Optional[TracebackType]) -> None
-        exception = format_exception(
-            type(error), error, getattr(error, "stack", None) or traceback
-        )
-        logger.error("".join(exception))
-        self.errors.append(error)
-
-    def get_sub_fields(self, return_type, field_asts):
-        # type: (GraphQLObjectType, List[Field]) -> DefaultOrderedDict
-        k = return_type, tuple(field_asts)
-        if k not in self._subfields_cache:
-            subfield_asts = DefaultOrderedDict(list)
-            visited_fragment_names = set()  # type: Set[str]
-            for field_ast in field_asts:
-                selection_set = field_ast.selection_set
-                if selection_set:
-                    subfield_asts = collect_fields(
-                        self,
-                        return_type,
-                        selection_set,
-                        subfield_asts,
-                        visited_fragment_names,
-                    )
-            self._subfields_cache[k] = subfield_asts
-        return self._subfields_cache[k]
-
-
-class SubscriberExecutionContext(object):
-    __slots__ = "exe_context", "errors"
-
-    def __init__(self, exe_context):
-        # type: (ExecutionContext) -> None
-        self.exe_context = exe_context
-        self.errors = []  # type: List[Exception]
-
-    def reset(self):
-        # type: () -> None
-        self.errors = []
-
-    def __getattr__(self, name):
-        # type: (str) -> Any
-        return getattr(self.exe_context, name)
-
-
-def get_operation_root_type(schema, operation):
-    # type: (GraphQLSchema, OperationDefinition) -> GraphQLObjectType
-    op = operation.operation
-    if op == "query":
-        return schema.get_query_type()
-
-    elif op == "mutation":
-        mutation_type = schema.get_mutation_type()
-
-        if not mutation_type:
-            raise GraphQLError("Schema is not configured for mutations", [operation])
-
-        return mutation_type
-
-    elif op == "subscription":
-        subscription_type = schema.get_subscription_type()
-
-        if not subscription_type:
-            raise GraphQLError(
-                "Schema is not configured for subscriptions", [operation]
-            )
-
-        return subscription_type
-
-    raise GraphQLError(
-        "Can only execute queries, mutations and subscriptions", [operation]
-    )
-
-
-def collect_fields(
-    ctx,  # type: ExecutionContext
-    runtime_type,  # type: GraphQLObjectType
-    selection_set,  # type: SelectionSet
-    fields,  # type: DefaultOrderedDict
-    prev_fragment_names,  # type: Set[str]
-):
-    # type: (...) -> DefaultOrderedDict
-    """
-    Given a selectionSet, adds all of the fields in that selection to
-    the passed in map of fields, and returns it at the end.
-
-    collect_fields requires the "runtime type" of an object. For a field which
-    returns and Interface or Union type, the "runtime type" will be the actual
-    Object type returned by that field.
-    """
-    for selection in selection_set.selections:
-        directives = selection.directives
-
-        if isinstance(selection, ast.Field):
-            if not should_include_node(ctx, directives):
-                continue
-
-            name = get_field_entry_key(selection)
-            fields[name].append(selection)
-
-        elif isinstance(selection, ast.InlineFragment):
-            if not should_include_node(
-                ctx, directives
-            ) or not does_fragment_condition_match(ctx, selection, runtime_type):
-                continue
-
-            collect_fields(
-                ctx, runtime_type, selection.selection_set, fields, prev_fragment_names
-            )
-
-        elif isinstance(selection, ast.FragmentSpread):
-            frag_name = selection.name.value
-
-            if frag_name in prev_fragment_names or not should_include_node(
-                ctx, directives
-            ):
-                continue
-
-            prev_fragment_names.add(frag_name)
-            fragment = ctx.fragments[frag_name]
-            frag_directives = fragment.directives
-            if (
-                not fragment
-                or not should_include_node(ctx, frag_directives)
-                or not does_fragment_condition_match(ctx, fragment, runtime_type)
-            ):
-                continue
-
-            collect_fields(
-                ctx, runtime_type, fragment.selection_set, fields, prev_fragment_names
-            )
-
-    return fields
-
-
-def should_include_node(ctx, directives):
-    # type: (ExecutionContext, Optional[List[Directive]]) -> bool
-    """Determines if a field should be included based on the @include and
-    @skip directives, where @skip has higher precidence than @include."""
-    # TODO: Refactor based on latest code
-    if directives:
-        skip_ast = None
-
-        for directive in directives:
-            if directive.name.value == GraphQLSkipDirective.name:
-                skip_ast = directive
-                break
-
-        if skip_ast:
-            args = get_argument_values(
-                GraphQLSkipDirective.args, skip_ast.arguments, ctx.variable_values
-            )
-            if args.get("if") is True:
-                return False
-
-        include_ast = None
-
-        for directive in directives:
-            if directive.name.value == GraphQLIncludeDirective.name:
-                include_ast = directive
-                break
-
-        if include_ast:
-            args = get_argument_values(
-                GraphQLIncludeDirective.args, include_ast.arguments, ctx.variable_values
-            )
-
-            if args.get("if") is False:
-                return False
-
-    return True
-
-
-def does_fragment_condition_match(
-    ctx,  # type: ExecutionContext
-    fragment,  # type: Union[FragmentDefinition, InlineFragment]
-    type_,  # type: GraphQLObjectType
-):
-    # type: (...) -> bool
-    type_condition_ast = fragment.type_condition
-    if not type_condition_ast:
-        return True
-
-    conditional_type = type_from_ast(ctx.schema, type_condition_ast)
-    if conditional_type.is_same_type(type_):
-        return True
-
-    if isinstance(conditional_type, (GraphQLInterfaceType, GraphQLUnionType)):
-        return ctx.schema.is_possible_type(conditional_type, type_)
-
-    return False
-
-
-def get_field_entry_key(node):
-    # type: (Field) -> str
-    """Implements the logic to compute the key of a given field's entry"""
-    if node.alias:
-        return node.alias.value
-    return node.name.value
-
-
-def default_resolve_fn(source, info, **args):
-    # type: (Any, ResolveInfo, **Any) -> Optional[Any]
-    """If a resolve function is not given, then a default resolve behavior is used which takes the property of the source object
-    of the same name as the field and returns it as the result, or if it's a function, returns the result of calling that function."""
-    name = info.field_name
-    if isinstance(source, dict):
-        property = source.get(name)
-    else:
-        property = getattr(source, name, None)
-    if callable(property):
-        return property()
-    return property
-
-
-def get_field_def(
-    schema,  # type: GraphQLSchema
-    parent_type,  # type: GraphQLObjectType
-    field_name,  # type: str
-):
-    # type: (...) -> Optional[GraphQLField]
-    """This method looks up the field on the given type defintion.
-    It has special casing for the two introspection fields, __schema
-    and __typename. __typename is special because it can always be
-    queried as a field, even in situations where no other fields
-    are allowed, like on a Union. __schema could get automatically
-    added to the query type, but that would require mutating type
-    definitions, which would cause issues."""
-    if field_name == "__schema" and schema.get_query_type() == parent_type:
-        return SchemaMetaFieldDef
-    elif field_name == "__type" and schema.get_query_type() == parent_type:
-        return TypeMetaFieldDef
-    elif field_name == "__typename":
-        return TypeNameMetaFieldDef
-    return parent_type.fields.get(field_name)
diff --git a/graphql/execution/values.py b/graphql/execution/values.py
deleted file mode 100644
index b4304cb..0000000
--- a/graphql/execution/values.py
+++ /dev/null
@@ -1,186 +0,0 @@
-try:
-    from collections.abc import Iterable
-except ImportError:  # Python <3.3
-    from collections import Iterable
-import json
-
-from six import string_types
-
-from ..error import GraphQLError
-from ..language import ast
-from ..language.printer import print_ast
-from ..type import (
-    GraphQLEnumType,
-    GraphQLInputObjectType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLScalarType,
-    is_input_type,
-)
-from ..utils.is_valid_value import is_valid_value
-from ..utils.type_from_ast import type_from_ast
-from ..utils.value_from_ast import value_from_ast
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.ast import VariableDefinition, Argument
-    from ..type.schema import GraphQLSchema
-    from ..type.definition import GraphQLArgument
-    from typing import Any, List, Union, Dict, Optional
-
-__all__ = ["get_variable_values", "get_argument_values"]
-
-
-def get_variable_values(
-    schema,  # type: GraphQLSchema
-    definition_asts,  # type: List[VariableDefinition]
-    inputs,  # type: Any
-):
-    # type: (...) -> Dict[str, Any]
-    """Prepares an object map of variables of the correct type based on the provided variable definitions and arbitrary input.
-    If the input cannot be parsed to match the variable definitions, a GraphQLError will be thrown."""
-    if inputs is None:
-        inputs = {}
-
-    values = {}
-    for def_ast in definition_asts:
-        var_name = def_ast.variable.name.value
-        var_type = type_from_ast(schema, def_ast.type)
-        value = inputs.get(var_name)
-
-        if not is_input_type(var_type):
-            raise GraphQLError(
-                'Variable "${var_name}" expected value of type "{var_type}" which cannot be used as an input type.'.format(
-                    var_name=var_name, var_type=print_ast(def_ast.type)
-                ),
-                [def_ast],
-            )
-        elif value is None:
-            if def_ast.default_value is not None:
-                values[var_name] = value_from_ast(
-                    def_ast.default_value, var_type
-                )  # type: ignore
-            if isinstance(var_type, GraphQLNonNull):
-                raise GraphQLError(
-                    'Variable "${var_name}" of required type "{var_type}" was not provided.'.format(
-                        var_name=var_name, var_type=var_type
-                    ),
-                    [def_ast],
-                )
-        else:
-            errors = is_valid_value(value, var_type)
-            if errors:
-                message = u"\n" + u"\n".join(errors)
-                raise GraphQLError(
-                    'Variable "${}" got invalid value {}.{}'.format(
-                        var_name, json.dumps(value, sort_keys=True), message
-                    ),
-                    [def_ast],
-                )
-            coerced_value = coerce_value(var_type, value)
-            if coerced_value is None:
-                raise Exception("Should have reported error.")
-
-            values[var_name] = coerced_value
-
-    return values
-
-
-def get_argument_values(
-    arg_defs,  # type: Union[Dict[str, GraphQLArgument], Dict]
-    arg_asts,  # type: Optional[List[Argument]]
-    variables=None,  # type: Optional[Dict[str, Union[List, Dict, int, float, bool, str, None]]]
-):
-    # type: (...) -> Dict[str, Any]
-    """Prepares an object map of argument values given a list of argument
-    definitions and list of argument AST nodes."""
-    if not arg_defs:
-        return {}
-
-    if arg_asts:
-        arg_ast_map = {
-            arg.name.value: arg for arg in arg_asts
-        }  # type: Dict[str, Argument]
-    else:
-        arg_ast_map = {}
-
-    result = {}
-    for name, arg_def in arg_defs.items():
-        arg_type = arg_def.type
-        arg_ast = arg_ast_map.get(name)
-        if name not in arg_ast_map:
-            if arg_def.default_value is not None:
-                result[arg_def.out_name or name] = arg_def.default_value
-                continue
-            elif isinstance(arg_type, GraphQLNonNull):
-                raise GraphQLError(
-                    'Argument "{name}" of required type {arg_type}" was not provided.'.format(
-                        name=name, arg_type=arg_type
-                    ),
-                    arg_asts,
-                )
-        elif isinstance(arg_ast.value, ast.Variable):  # type: ignore
-            variable_name = arg_ast.value.name.value  # type: ignore
-            if variables and variable_name in variables:
-                result[arg_def.out_name or name] = variables[variable_name]
-            elif arg_def.default_value is not None:
-                result[arg_def.out_name or name] = arg_def.default_value
-            elif isinstance(arg_type, GraphQLNonNull):
-                raise GraphQLError(
-                    'Argument "{name}" of required type {arg_type}" provided the variable "${variable_name}" which was not provided'.format(
-                        name=name, arg_type=arg_type, variable_name=variable_name
-                    ),
-                    arg_asts,
-                )
-            continue
-
-        else:
-            value = value_from_ast(arg_ast.value, arg_type, variables)  # type: ignore
-            if value is None:
-                if arg_def.default_value is not None:
-                    value = arg_def.default_value
-                    result[arg_def.out_name or name] = value
-            else:
-                # We use out_name as the output name for the
-                # dict if exists
-                result[arg_def.out_name or name] = value
-
-    return result
-
-
-def coerce_value(type, value):
-    # type: (Any, Any) -> Union[List, Dict, int, float, bool, str, None]
-    """Given a type and any value, return a runtime value coerced to match the type."""
-    if isinstance(type, GraphQLNonNull):
-        # Note: we're not checking that the result of coerceValue is
-        # non-null.
-        # We only call this function after calling isValidValue.
-        return coerce_value(type.of_type, value)
-
-    if value is None:
-        return None
-
-    if isinstance(type, GraphQLList):
-        item_type = type.of_type
-        if not isinstance(value, string_types) and isinstance(value, Iterable):
-            return [coerce_value(item_type, item) for item in value]
-        else:
-            return [coerce_value(item_type, value)]
-
-    if isinstance(type, GraphQLInputObjectType):
-        fields = type.fields
-        obj = {}
-        for field_name, field in fields.items():
-            if field_name not in value:
-                if field.default_value is not None:
-                    field_value = field.default_value
-                    obj[field.out_name or field_name] = field_value
-            else:
-                field_value = coerce_value(field.type, value.get(field_name))
-                obj[field.out_name or field_name] = field_value
-
-        return type.create_container(obj)
-
-    assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), "Must be input type"
-
-    return type.parse_value(value)
diff --git a/graphql/flags.py b/graphql/flags.py
deleted file mode 100644
index e3f3e62..0000000
--- a/graphql/flags.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# This file makes it easier to know what are the features
-# the GraphQL-core API supports
-
-# This permits to plug different backend when executing graphql(...)
-BACKEND_EXECUTOR = True
-
-# This add a new path attribute to ResolveInfo, filled with the
-# path of the field where is being executed
-PATH_IN_RESOLVEINFO = True
diff --git a/graphql/graphql.py b/graphql/graphql.py
deleted file mode 100644
index 89ccf38..0000000
--- a/graphql/graphql.py
+++ /dev/null
@@ -1,78 +0,0 @@
-from .execution import ExecutionResult
-from .backend import get_default_backend
-
-from promise import promisify
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from promise import Promise
-    from rx import Observable
-    from typing import Any, Union, Optional
-    from .language.ast import Document
-    from .type.schema import GraphQLSchema
-
-# This is the primary entry point function for fulfilling GraphQL operations
-# by parsing, validating, and executing a GraphQL document along side a
-# GraphQL schema.
-
-# More sophisticated GraphQL servers, such as those which persist queries,
-# may wish to separate the validation and execution phases to a static time
-# tooling step, and a server runtime step.
-
-# schema:
-#    The GraphQL type system to use when validating and executing a query.
-# requestString:
-#    A GraphQL language formatted string representing the requested operation.
-# rootValue:
-#    The value provided as the first argument to resolver functions on the top
-#    level type (e.g. the query object type).
-# variableValues:
-#    A mapping of variable name to runtime value to use for all variables
-#    defined in the requestString.
-# operationName:
-#    The name of the operation to use if requestString contains multiple
-#    possible operations. Can be omitted if requestString contains only
-#    one operation.
-
-
-def graphql(*args, **kwargs):
-    # type: (*Any, **Any) -> Union[ExecutionResult, Observable, Promise[ExecutionResult]]
-    return_promise = kwargs.get("return_promise", False)
-    if return_promise:
-        return execute_graphql_as_promise(*args, **kwargs)
-    else:
-        return execute_graphql(*args, **kwargs)
-
-
-def execute_graphql(
-    schema,  # type: GraphQLSchema
-    request_string="",  # type: Union[Document, str]
-    root=None,  # type: Any
-    context=None,  # type: Optional[Any]
-    variables=None,  # type: Optional[Any]
-    operation_name=None,  # type: Optional[Any]
-    middleware=None,  # type: Optional[Any]
-    backend=None,  # type: Optional[Any]
-    **execute_options  # type: Any
-):
-    # type: (...) -> Union[ExecutionResult, Observable, Promise[ExecutionResult]]
-    try:
-        if backend is None:
-            backend = get_default_backend()
-
-        document = backend.document_from_string(schema, request_string)
-        return document.execute(
-            root=root,
-            context=context,
-            operation_name=operation_name,
-            variables=variables,
-            middleware=middleware,
-            **execute_options
-        )
-    except Exception as e:
-        return ExecutionResult(errors=[e], invalid=True)
-
-
-@promisify
-def execute_graphql_as_promise(*args, **kwargs):
-    return execute_graphql(*args, **kwargs)
diff --git a/graphql/language/__init__.py b/graphql/language/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/language/ast.py b/graphql/language/ast.py
deleted file mode 100644
index 0c40d44..0000000
--- a/graphql/language/ast.py
+++ /dev/null
@@ -1,1499 +0,0 @@
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from .parser import Loc
-    from typing import Any, Optional, Union, List, Iterable
-
-# This is autogenerated code. DO NOT change this manually.
-# Run scripts/generate_ast.py to generate this file.
-
-
-class Node(object):
-    __slots__ = ()
-    _fields = ()  # type: Iterable[str]
-    loc = None  # type: Optional[Loc]
-
-
-class Definition(Node):
-    __slots__ = ()
-
-
-class Document(Node):
-    __slots__ = ("loc", "definitions")
-    _fields = ("definitions",)
-
-    def __init__(self, definitions, loc=None):
-        # type: (Any, Optional[Loc]) -> None
-        self.loc = loc
-        self.definitions = definitions
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, Document)
-            and
-            # self.loc == other.loc and
-            self.definitions == other.definitions
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("Document(" "definitions={self.definitions!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> Document
-        return type(self)(self.definitions, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class OperationDefinition(Definition):
-    __slots__ = (
-        "loc",
-        "operation",
-        "name",
-        "variable_definitions",
-        "directives",
-        "selection_set",
-    )
-    _fields = (
-        "operation",
-        "name",
-        "variable_definitions",
-        "directives",
-        "selection_set",
-    )
-
-    def __init__(
-        self,
-        operation,  # type: str
-        selection_set,  # type: SelectionSet
-        name=None,  # type: Optional[Name]
-        #
-        variable_definitions=None,  # type: Optional[List[VariableDefinition]]
-        directives=None,  # type: Optional[List[Directive]]
-        loc=None,  # type: Optional[Loc]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.operation = operation
-        self.name = name
-        self.variable_definitions = variable_definitions
-        self.directives = directives
-        self.selection_set = selection_set
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, OperationDefinition)
-            and
-            # self.loc == other.loc and
-            self.operation == other.operation
-            and self.name == other.name
-            and self.variable_definitions == other.variable_definitions
-            and self.directives == other.directives
-            and self.selection_set == other.selection_set
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "OperationDefinition("
-            "operation={self.operation!r}"
-            ", name={self.name!r}"
-            ", variable_definitions={self.variable_definitions!r}"
-            ", directives={self.directives!r}"
-            ", selection_set={self.selection_set!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> OperationDefinition
-        return type(self)(
-            self.operation,
-            self.selection_set,
-            self.name,
-            self.variable_definitions,
-            self.directives,
-            self.loc,
-        )
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class VariableDefinition(Node):
-    __slots__ = ("loc", "variable", "type", "default_value")
-    _fields = ("variable", "type", "default_value")
-
-    def __init__(self, variable, type, default_value=None, loc=None):
-        # type: (Variable, Any, Any, Optional[Loc]) -> None
-        self.loc = loc
-        self.variable = variable
-        self.type = type
-        self.default_value = default_value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, VariableDefinition)
-            and
-            # self.loc == other.loc and
-            self.variable == other.variable
-            and self.type == other.type
-            and self.default_value == other.default_value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "VariableDefinition("
-            "variable={self.variable!r}"
-            ", type={self.type!r}"
-            ", default_value={self.default_value!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> VariableDefinition
-        return type(self)(self.variable, self.type, self.default_value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class SelectionSet(Node):
-    __slots__ = ("loc", "selections")
-    _fields = ("selections",)
-
-    def __init__(self, selections, loc=None):
-        # type: (Any, Optional[Loc]) -> None
-        self.loc = loc
-        self.selections = selections
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, SelectionSet)
-            and
-            # self.loc == other.loc and
-            self.selections == other.selections
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("SelectionSet(" "selections={self.selections!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> SelectionSet
-        return type(self)(self.selections, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class Selection(Node):
-    __slots__ = ()
-
-
-class Field(Selection):
-    __slots__ = ("loc", "alias", "name", "arguments", "directives", "selection_set")
-    _fields = ("alias", "name", "arguments", "directives", "selection_set")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        alias=None,  # type: Optional[Name]
-        arguments=None,  # type: Optional[List[Argument]]
-        directives=None,  # type: Optional[List[Directive]]
-        selection_set=None,  # type: Optional[SelectionSet]
-        loc=None,  # type: Optional[Loc]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.alias = alias
-        self.name = name
-        self.arguments = arguments
-        self.directives = directives
-        self.selection_set = selection_set
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, Field)
-            and
-            # self.loc == other.loc and
-            self.alias == other.alias
-            and self.name == other.name
-            and self.arguments == other.arguments
-            and self.directives == other.directives
-            and self.selection_set == other.selection_set
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "Field("
-            "alias={self.alias!r}"
-            ", name={self.name!r}"
-            ", arguments={self.arguments!r}"
-            ", directives={self.directives!r}"
-            ", selection_set={self.selection_set!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> Field
-        return type(self)(
-            self.name,
-            self.alias,
-            self.arguments,
-            self.directives,
-            self.selection_set,
-            self.loc,
-        )
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class Argument(Node):
-    __slots__ = ("loc", "name", "value")
-    _fields = ("name", "value")
-
-    def __init__(self, name, value, loc=None):
-        # type: (Name, Any, Optional[Loc]) -> None
-        self.loc = loc
-        self.name = name
-        self.value = value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, Argument)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.value == other.value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("Argument(" "name={self.name!r}" ", value={self.value!r}" ")").format(
-            self=self
-        )
-
-    def __copy__(self):
-        # type: () -> Argument
-        return type(self)(self.name, self.value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class FragmentSpread(Selection):
-    __slots__ = ("loc", "name", "directives")
-    _fields = ("name", "directives")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        directives=None,  # type: List[Directive]
-        loc=None,  # type: Optional[Loc]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, FragmentSpread)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "FragmentSpread("
-            "name={self.name!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> FragmentSpread
-        return type(self)(self.name, self.directives, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class InlineFragment(Selection):
-    __slots__ = ("loc", "type_condition", "directives", "selection_set")
-    _fields = ("type_condition", "directives", "selection_set")
-
-    def __init__(
-        self,
-        type_condition,  # type: Optional[NamedType]
-        selection_set,  # type: SelectionSet
-        directives=None,  # type: Optional[List[Directive]]
-        loc=None,  # type: Optional[Loc]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.type_condition = type_condition
-        self.directives = directives
-        self.selection_set = selection_set
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, InlineFragment)
-            and
-            # self.loc == other.loc and
-            self.type_condition == other.type_condition
-            and self.directives == other.directives
-            and self.selection_set == other.selection_set
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "InlineFragment("
-            "type_condition={self.type_condition!r}"
-            ", directives={self.directives!r}"
-            ", selection_set={self.selection_set!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> InlineFragment
-        return type(self)(
-            self.type_condition, self.selection_set, self.directives, self.loc
-        )
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class FragmentDefinition(Definition):
-    __slots__ = ("loc", "name", "type_condition", "directives", "selection_set")
-    _fields = ("name", "type_condition", "directives", "selection_set")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        type_condition,  # type: Optional[NamedType]
-        selection_set,  # type: SelectionSet
-        directives=None,  # type: Optional[List[Directive]]
-        loc=None,  # type: Optional[Loc]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.type_condition = type_condition
-        self.directives = directives
-        self.selection_set = selection_set
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, FragmentDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.type_condition == other.type_condition
-            and self.directives == other.directives
-            and self.selection_set == other.selection_set
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "FragmentDefinition("
-            "name={self.name!r}"
-            ", type_condition={self.type_condition!r}"
-            ", directives={self.directives!r}"
-            ", selection_set={self.selection_set!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> FragmentDefinition
-        return type(self)(
-            self.name,
-            self.type_condition,
-            self.selection_set,
-            self.directives,
-            self.loc,
-        )
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class Value(Node):
-    __slots__ = ()
-
-
-class Variable(Value):
-    __slots__ = ("loc", "name")
-    _fields = ("name",)
-
-    def __init__(self, name, loc=None):
-        # type: (Name, Optional[Loc]) -> None
-        self.loc = loc
-        self.name = name
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, Variable)
-            and self.name == other.name
-            # and self.loc == other.loc
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("Variable(" "name={self.name!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> Variable
-        return type(self)(self.name, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class IntValue(Value):
-    __slots__ = ("loc", "value")
-    _fields = ("value",)
-
-    def __init__(self, value, loc=None):
-        # type: (str, Optional[Loc]) -> None
-        self.loc = loc
-        self.value = value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, IntValue)
-            and
-            # self.loc == other.loc and
-            self.value == other.value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("IntValue(" "value={self.value!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> IntValue
-        return type(self)(self.value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class FloatValue(Value):
-    __slots__ = ("loc", "value")
-    _fields = ("value",)
-
-    def __init__(self, value, loc=None):
-        # type: (str, Optional[Any]) -> None
-        self.loc = loc
-        self.value = value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, FloatValue)
-            and
-            # self.loc == other.loc and
-            self.value == other.value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("FloatValue(" "value={self.value!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> FloatValue
-        return type(self)(self.value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class StringValue(Value):
-    __slots__ = ("loc", "value")
-    _fields = ("value",)
-
-    def __init__(self, value, loc=None):
-        # type: (str, Optional[Loc]) -> None
-        self.loc = loc
-        self.value = value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, StringValue)
-            and
-            # self.loc == other.loc and
-            self.value == other.value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("StringValue(" "value={self.value!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> StringValue
-        return type(self)(self.value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class BooleanValue(Value):
-    __slots__ = ("loc", "value")
-    _fields = ("value",)
-
-    def __init__(self, value, loc=None):
-        # type: (bool, Optional[Loc]) -> None
-        self.loc = loc
-        self.value = value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, BooleanValue)
-            and
-            # self.loc == other.loc and
-            self.value == other.value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("BooleanValue(" "value={self.value!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> BooleanValue
-        return type(self)(self.value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class EnumValue(Value):
-    __slots__ = ("loc", "value")
-    _fields = ("value",)
-
-    def __init__(self, value, loc=None):
-        # type: (str, Optional[Loc]) -> None
-        self.loc = loc
-        self.value = value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, EnumValue)
-            and
-            # self.loc == other.loc and
-            self.value == other.value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("EnumValue(" "value={self.value!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> EnumValue
-        return type(self)(self.value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class ListValue(Value):
-    __slots__ = ("loc", "values")
-    _fields = ("values",)
-
-    def __init__(self, values, loc=None):
-        # type: (Any, Optional[Loc]) -> None
-        self.loc = loc
-        self.values = values
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, ListValue)
-            and
-            # self.loc == other.loc and
-            self.values == other.values
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("ListValue(" "values={self.values!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> ListValue
-        return type(self)(self.values, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class ObjectValue(Value):
-    __slots__ = ("loc", "fields")
-    _fields = ("fields",)
-
-    def __init__(self, fields, loc=None):
-        # type: (List[ObjectField], Optional[Loc]) -> None
-        self.loc = loc
-        self.fields = fields
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, ObjectValue)
-            and
-            # self.loc == other.loc and
-            self.fields == other.fields
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("ObjectValue(" "fields={self.fields!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> ObjectValue
-        return type(self)(self.fields, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class ObjectField(Node):
-    __slots__ = ("loc", "name", "value")
-    _fields = ("name", "value")
-
-    def __init__(self, name, value, loc=None):
-        # type: (Name, Any, Optional[Loc]) -> None
-        self.loc = loc
-        self.name = name
-        self.value = value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, ObjectField)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.value == other.value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "ObjectField(" "name={self.name!r}" ", value={self.value!r}" ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> ObjectField
-        return type(self)(self.name, self.value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class Directive(Node):
-    __slots__ = ("loc", "name", "arguments")
-    _fields = ("name", "arguments")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        arguments=None,  # type: Optional[List[Argument]]
-        loc=None,  # type: Optional[Loc]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.arguments = arguments
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, Directive)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.arguments == other.arguments
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "Directive(" "name={self.name!r}" ", arguments={self.arguments!r}" ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> Directive
-        return type(self)(self.name, self.arguments, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class Type(Node):
-    __slots__ = ()
-
-
-class NamedType(Type):
-    __slots__ = ("loc", "name")
-    _fields = ("name",)
-
-    def __init__(self, name, loc=None):
-        # type: (Name, Optional[Loc]) -> None
-        self.loc = loc
-        self.name = name
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, NamedType)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("NamedType(" "name={self.name!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> NamedType
-        return type(self)(self.name, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class ListType(Type):
-    __slots__ = ("loc", "type")
-    _fields = ("type",)
-
-    def __init__(self, type, loc=None):
-        # type: (Union[NamedType, NonNullType], Optional[Loc]) -> None
-        self.loc = loc
-        self.type = type
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, ListType)
-            and
-            # self.loc == other.loc and
-            self.type == other.type
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("ListType(" "type={self.type!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> ListType
-        return type(self)(self.type, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class NonNullType(Type):
-    __slots__ = ("loc", "type")
-    _fields = ("type",)
-
-    def __init__(self, type, loc=None):
-        # type: (Union[ListType, NamedType], Optional[Loc]) -> None
-        self.loc = loc
-        self.type = type
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, NonNullType)
-            and
-            # self.loc == other.loc and
-            self.type == other.type
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("NonNullType(" "type={self.type!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> NonNullType
-        return type(self)(self.type, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class Name(Node):
-    __slots__ = ("loc", "value")
-    _fields = ("value",)
-
-    def __init__(self, value, loc=None):
-        # type: (str, Optional[Loc]) -> None
-        self.loc = loc
-        self.value = value
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, Name)
-            and
-            # self.loc == other.loc and
-            self.value == other.value
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("Name(" "value={self.value!r}" ")").format(self=self)
-
-    def __copy__(self):
-        # type: () -> Name
-        return type(self)(self.value, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-# Type System Definition
-
-
-class TypeDefinition(Node):
-    pass
-
-
-class TypeSystemDefinition(TypeDefinition):
-    pass
-
-
-class SchemaDefinition(TypeSystemDefinition):
-    __slots__ = ("loc", "directives", "operation_types")
-    _fields = ("operation_types",)
-
-    def __init__(
-        self,
-        operation_types,  # type: List[OperationTypeDefinition]
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List[Directive]]
-    ):
-        # type: (...) -> None
-        self.operation_types = operation_types
-        self.loc = loc
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, SchemaDefinition)
-            and self.operation_types == other.operation_types
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "SchemaDefinition("
-            "operation_types={self.operation_types!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> SchemaDefinition
-        return type(self)(self.operation_types, self.loc, self.directives)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class OperationTypeDefinition(Node):
-    __slots__ = ("loc", "operation", "type")
-    _fields = ("operation", "type")
-
-    def __init__(self, operation, type, loc=None):
-        # type: (str, NamedType, Optional[Loc]) -> None
-        self.operation = operation
-        self.type = type
-        self.loc = loc
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, OperationTypeDefinition)
-            and self.operation == other.operation
-            and self.type == other.type
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "OperationTypeDefinition("
-            "operation={self.operation!r}"
-            ", type={self.type!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> OperationTypeDefinition
-        return type(self)(self.operation, self.type, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class ObjectTypeDefinition(TypeDefinition):
-    __slots__ = ("loc", "name", "interfaces", "directives", "fields")
-    _fields = ("name", "interfaces", "fields")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        fields,  # type: List[FieldDefinition]
-        interfaces=None,  # type: Optional[List[NamedType]]
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List[Directive]]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.interfaces = interfaces
-        self.fields = fields
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, ObjectTypeDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.interfaces == other.interfaces
-            and self.fields == other.fields
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "ObjectTypeDefinition("
-            "name={self.name!r}"
-            ", interfaces={self.interfaces!r}"
-            ", fields={self.fields!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> ObjectTypeDefinition
-        return type(self)(
-            self.name, self.fields, self.interfaces, self.loc, self.directives
-        )
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class FieldDefinition(Node):
-    __slots__ = ("loc", "name", "arguments", "type", "directives")
-    _fields = ("name", "arguments", "type")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        arguments,  # type: List[InputValueDefinition]
-        type,  # type: Union[NamedType, NonNullType, ListType]
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.arguments = arguments
-        self.type = type
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, FieldDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.arguments == other.arguments
-            and self.type == other.type
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "FieldDefinition("
-            "name={self.name!r}"
-            ", arguments={self.arguments!r}"
-            ", type={self.type!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> FieldDefinition
-        return type(self)(
-            self.name, self.arguments, self.type, self.loc, self.directives
-        )
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class InputValueDefinition(Node):
-    __slots__ = ("loc", "name", "type", "default_value", "directives")
-    _fields = ("name", "type", "default_value")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        type,  # type: Union[NamedType, NonNullType, ListType]
-        default_value=None,  # type: Any
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.type = type
-        self.default_value = default_value
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, InputValueDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.type == other.type
-            and self.default_value == other.default_value
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "InputValueDefinition("
-            "name={self.name!r}"
-            ", type={self.type!r}"
-            ", default_value={self.default_value!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> InputValueDefinition
-        return type(self)(
-            self.name, self.type, self.default_value, self.loc, self.directives
-        )
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class InterfaceTypeDefinition(TypeDefinition):
-    __slots__ = ("loc", "name", "fields", "directives")
-    _fields = ("name", "fields")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        fields,  # type: List[FieldDefinition]
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List[Directive]]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.fields = fields
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, InterfaceTypeDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.fields == other.fields
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "InterfaceTypeDefinition("
-            "name={self.name!r}"
-            ", fields={self.fields!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> InterfaceTypeDefinition
-        return type(self)(self.name, self.fields, self.loc, self.directives)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class UnionTypeDefinition(TypeDefinition):
-    __slots__ = ("loc", "name", "types", "directives")
-    _fields = ("name", "types")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        types,  # type: List[NamedType]
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List[Directive]]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.types = types
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, UnionTypeDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.types == other.types
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "UnionTypeDefinition("
-            "name={self.name!r}"
-            ", types={self.types!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> UnionTypeDefinition
-        return type(self)(self.name, self.types, self.loc, self.directives)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class ScalarTypeDefinition(TypeDefinition):
-    __slots__ = ("loc", "name", "directives")
-    _fields = ("name",)
-
-    def __init__(
-        self,
-        name,  # type: Name
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List[Directive]]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, ScalarTypeDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "ScalarTypeDefinition("
-            "name={self.name!r}"
-            "directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> ScalarTypeDefinition
-        return type(self)(self.name, self.loc, self.directives)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class EnumTypeDefinition(TypeDefinition):
-    __slots__ = ("loc", "name", "values", "directives")
-    _fields = ("name", "values")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        values,  # type: List[EnumValueDefinition]
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List[Directive]]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.values = values
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, EnumTypeDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.values == other.values
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "EnumTypeDefinition("
-            "name={self.name!r}"
-            ", values={self.values!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> EnumTypeDefinition
-        return type(self)(self.name, self.values, self.loc, self.directives)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class EnumValueDefinition(Node):
-    __slots__ = ("loc", "name", "directives")
-    _fields = ("name",)
-
-    def __init__(
-        self,
-        name,  # type: Name
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List[Directive]]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, EnumValueDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "EnumValueDefinition("
-            "name={self.name!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> EnumValueDefinition
-        return type(self)(self.name, self.loc, self.directives)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class InputObjectTypeDefinition(TypeDefinition):
-    __slots__ = ("loc", "name", "fields", "directives")
-    _fields = ("name", "fields")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        fields,  # type: List[InputValueDefinition]
-        loc=None,  # type: Optional[Loc]
-        directives=None,  # type: Optional[List[Directive]]
-    ):
-        # type: (...) -> None
-        self.loc = loc
-        self.name = name
-        self.fields = fields
-        self.directives = directives
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, InputObjectTypeDefinition)
-            and
-            # self.loc == other.loc and
-            self.name == other.name
-            and self.fields == other.fields
-            and self.directives == other.directives
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "InputObjectTypeDefinition("
-            "name={self.name!r}"
-            ", fields={self.fields!r}"
-            ", directives={self.directives!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> InputObjectTypeDefinition
-        return type(self)(self.name, self.fields, self.loc, self.directives)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class TypeExtensionDefinition(TypeSystemDefinition):
-    __slots__ = ("loc", "definition")
-    _fields = ("definition",)
-
-    def __init__(self, definition, loc=None):
-        # type: (ObjectTypeDefinition, Optional[Loc]) -> None
-        self.loc = loc
-        self.definition = definition
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, TypeExtensionDefinition)
-            and
-            # self.loc == other.loc and
-            self.definition == other.definition
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return ("TypeExtensionDefinition(" "definition={self.definition!r}" ")").format(
-            self=self
-        )
-
-    def __copy__(self):
-        # type: () -> TypeExtensionDefinition
-        return type(self)(self.definition, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-
-class DirectiveDefinition(TypeSystemDefinition):
-    __slots__ = ("loc", "name", "arguments", "locations")
-    _fields = ("name", "locations")
-
-    def __init__(
-        self,
-        name,  # type: Name
-        locations,  # type: List[Name]
-        arguments=None,  # type: Optional[List[InputValueDefinition]]
-        loc=None,  # type: Optional[Loc]
-    ):
-        # type: (...) -> None
-        self.name = name
-        self.locations = locations
-        self.loc = loc
-        self.arguments = arguments
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return self is other or (
-            isinstance(other, DirectiveDefinition)
-            and self.name == other.name
-            and self.locations == other.locations
-            and
-            # self.loc == other.loc and
-            self.arguments == other.arguments
-        )
-
-    def __repr__(self):
-        # type: () -> str
-        return (
-            "DirectiveDefinition("
-            "name={self.name!r}, "
-            "locations={self.locations!r}"
-            ")"
-        ).format(self=self)
-
-    def __copy__(self):
-        # type: () -> DirectiveDefinition
-        return type(self)(self.name, self.locations, self.arguments, self.loc)
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
diff --git a/graphql/language/base.py b/graphql/language/base.py
deleted file mode 100644
index fca28dc..0000000
--- a/graphql/language/base.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from .lexer import Lexer
-from .location import get_location
-from .parser import parse, parse_value
-from .printer import print_ast
-from .source import Source
-from .visitor import BREAK, ParallelVisitor, TypeInfoVisitor, visit
-
-__all__ = [
-    "Lexer",
-    "get_location",
-    "parse",
-    "parse_value",
-    "print_ast",
-    "Source",
-    "BREAK",
-    "ParallelVisitor",
-    "TypeInfoVisitor",
-    "visit",
-]
diff --git a/graphql/language/lexer.py b/graphql/language/lexer.py
deleted file mode 100644
index 45cd668..0000000
--- a/graphql/language/lexer.py
+++ /dev/null
@@ -1,475 +0,0 @@
-import json
-
-from six import unichr
-
-from ..error import GraphQLSyntaxError
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Optional, Any, List
-    from .source import Source
-
-__all__ = ["Token", "Lexer", "TokenKind", "get_token_desc", "get_token_kind_desc"]
-
-
-class Token(object):
-    __slots__ = "kind", "start", "end", "value"
-
-    def __init__(self, kind, start, end, value=None):
-        # type: (int, int, int, Optional[str]) -> None
-        self.kind = kind
-        self.start = start
-        self.end = end
-        self.value = value
-
-    def __repr__(self):
-        # type: () -> str
-        return u"<Token kind={} at {}..{} value={}>".format(
-            get_token_kind_desc(self.kind), self.start, self.end, repr(self.value)
-        )
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return (
-            isinstance(other, Token)
-            and self.kind == other.kind
-            and self.start == other.start
-            and self.end == other.end
-            and self.value == other.value
-        )
-
-
-class Lexer(object):
-    __slots__ = "source", "prev_position"
-
-    def __init__(self, source):
-        # type: (Source) -> None
-        self.source = source
-        self.prev_position = 0
-
-    def next_token(self, reset_position=None):
-        # type: (Optional[int]) -> Token
-        if reset_position is None:
-            reset_position = self.prev_position
-        token = read_token(self.source, reset_position)
-        self.prev_position = token.end
-        return token
-
-
-class TokenKind(object):
-    EOF = 1
-    BANG = 2
-    DOLLAR = 3
-    PAREN_L = 4
-    PAREN_R = 5
-    SPREAD = 6
-    COLON = 7
-    EQUALS = 8
-    AT = 9
-    BRACKET_L = 10
-    BRACKET_R = 11
-    BRACE_L = 12
-    PIPE = 13
-    BRACE_R = 14
-    NAME = 15
-    VARIABLE = 16
-    INT = 17
-    FLOAT = 18
-    STRING = 19
-
-
-def get_token_desc(token):
-    # type: (Token) -> str
-    if token.value:
-        return u'{} "{}"'.format(get_token_kind_desc(token.kind), token.value)
-    else:
-        return get_token_kind_desc(token.kind)
-
-
-def get_token_kind_desc(kind):
-    # type: (int) -> str
-    return TOKEN_DESCRIPTION[kind]
-
-
-TOKEN_DESCRIPTION = {
-    TokenKind.EOF: "EOF",
-    TokenKind.BANG: "!",
-    TokenKind.DOLLAR: "$",
-    TokenKind.PAREN_L: "(",
-    TokenKind.PAREN_R: ")",
-    TokenKind.SPREAD: "...",
-    TokenKind.COLON: ":",
-    TokenKind.EQUALS: "=",
-    TokenKind.AT: "@",
-    TokenKind.BRACKET_L: "[",
-    TokenKind.BRACKET_R: "]",
-    TokenKind.BRACE_L: "{",
-    TokenKind.PIPE: "|",
-    TokenKind.BRACE_R: "}",
-    TokenKind.NAME: "Name",
-    TokenKind.VARIABLE: "Variable",
-    TokenKind.INT: "Int",
-    TokenKind.FLOAT: "Float",
-    TokenKind.STRING: "String",
-}
-
-
-def char_code_at(s, pos):
-    # type: (str, int) -> Optional[int]
-    if 0 <= pos < len(s):
-        return ord(s[pos])
-
-    return None
-
-
-PUNCT_CODE_TO_KIND = {
-    ord("!"): TokenKind.BANG,
-    ord("$"): TokenKind.DOLLAR,
-    ord("("): TokenKind.PAREN_L,
-    ord(")"): TokenKind.PAREN_R,
-    ord(":"): TokenKind.COLON,
-    ord("="): TokenKind.EQUALS,
-    ord("@"): TokenKind.AT,
-    ord("["): TokenKind.BRACKET_L,
-    ord("]"): TokenKind.BRACKET_R,
-    ord("{"): TokenKind.BRACE_L,
-    ord("|"): TokenKind.PIPE,
-    ord("}"): TokenKind.BRACE_R,
-}
-
-
-def print_char_code(code):
-    # type: (Optional[int]) -> str
-    if code is None:
-        return "<EOF>"
-
-    if code < 0x007F:
-        return json.dumps(unichr(code))
-
-    return '"\\u%04X"' % code
-
-
-def read_token(source, from_position):
-    # type: (Source, int) -> Token
-    """Gets the next token from the source starting at the given position.
-
-    This skips over whitespace and comments until it finds the next lexable
-    token, then lexes punctuators immediately or calls the appropriate
-    helper fucntion for more complicated tokens."""
-    body = source.body
-    body_length = len(body)
-
-    position = position_after_whitespace(body, from_position)
-
-    if position >= body_length:
-        return Token(TokenKind.EOF, position, position)
-
-    code = char_code_at(body, position)
-    if code:
-        if code < 0x0020 and code not in (0x0009, 0x000A, 0x000D):
-            raise GraphQLSyntaxError(
-                source, position, u"Invalid character {}.".format(print_char_code(code))
-            )
-
-        kind = PUNCT_CODE_TO_KIND.get(code)
-        if kind is not None:
-            return Token(kind, position, position + 1)
-
-        if code == 46:  # .
-            if (
-                char_code_at(body, position + 1)
-                == char_code_at(body, position + 2)
-                == 46
-            ):
-                return Token(TokenKind.SPREAD, position, position + 3)
-
-        elif 65 <= code <= 90 or code == 95 or 97 <= code <= 122:
-            # A-Z, _, a-z
-            return read_name(source, position)
-
-        elif code == 45 or 48 <= code <= 57:  # -, 0-9
-            return read_number(source, position, code)
-
-        elif code == 34:  # "
-            return read_string(source, position)
-
-    raise GraphQLSyntaxError(
-        source, position, u"Unexpected character {}.".format(print_char_code(code))
-    )
-
-
-ignored_whitespace_characters = frozenset(
-    [
-        # BOM
-        0xFEFF,
-        # White Space
-        0x0009,  # tab
-        0x0020,  # space
-        # Line Terminator
-        0x000A,  # new line
-        0x000D,  # carriage return
-        # Comma
-        0x002C,
-    ]
-)
-
-
-def position_after_whitespace(body, start_position):
-    # type: (str, int) -> int
-    """Reads from body starting at start_position until it finds a
-    non-whitespace or commented character, then returns the position of
-    that character for lexing."""
-    body_length = len(body)
-    position = start_position
-    while position < body_length:
-        code = char_code_at(body, position)
-        if code in ignored_whitespace_characters:
-            position += 1
-
-        elif code == 35:  # #, skip comments
-            position += 1
-            while position < body_length:
-                code = char_code_at(body, position)
-                if not (
-                    code is not None
-                    and (code > 0x001F or code == 0x0009)
-                    and code not in (0x000A, 0x000D)
-                ):
-                    break
-
-                position += 1
-        else:
-            break
-    return position
-
-
-def read_number(source, start, first_code):
-    # type: (Source, int, Optional[int]) -> Token
-    r"""Reads a number token from the source file, either a float
-    or an int depending on whether a decimal point appears.
-
-    Int:   -?(0|[1-9][0-9]*)
-    Float: -?(0|[1-9][0-9]*)(\.[0-9]+)?((E|e)(+|-)?[0-9]+)?"""
-    code = first_code
-    body = source.body
-    position = start
-    is_float = False
-
-    if code == 45:  # -
-        position += 1
-        code = char_code_at(body, position)
-
-    if code == 48:  # 0
-        position += 1
-        code = char_code_at(body, position)
-
-        if code is not None and 48 <= code <= 57:
-            raise GraphQLSyntaxError(
-                source,
-                position,
-                u"Invalid number, unexpected digit after 0: {}.".format(
-                    print_char_code(code)
-                ),
-            )
-    else:
-        position = read_digits(source, position, code)
-        code = char_code_at(body, position)
-
-    if code == 46:  # .
-        is_float = True
-
-        position += 1
-        code = char_code_at(body, position)
-        position = read_digits(source, position, code)
-        code = char_code_at(body, position)
-
-    if code in (69, 101):  # E e
-        is_float = True
-        position += 1
-        code = char_code_at(body, position)
-        if code in (43, 45):  # + -
-            position += 1
-            code = char_code_at(body, position)
-
-        position = read_digits(source, position, code)
-
-    return Token(
-        TokenKind.FLOAT if is_float else TokenKind.INT,
-        start,
-        position,
-        body[start:position],
-    )
-
-
-def read_digits(source, start, first_code):
-    # type: (Source, int, Optional[int]) -> int
-    body = source.body
-    position = start
-    code = first_code
-
-    if code is not None and 48 <= code <= 57:  # 0 - 9
-        while True:
-            position += 1
-            code = char_code_at(body, position)
-
-            if not (code is not None and 48 <= code <= 57):
-                break
-
-        return position
-
-    raise GraphQLSyntaxError(
-        source,
-        position,
-        u"Invalid number, expected digit but got: {}.".format(print_char_code(code)),
-    )
-
-
-ESCAPED_CHAR_CODES = {
-    34: '"',
-    47: "/",
-    92: "\\",
-    98: "\b",
-    102: "\f",
-    110: "\n",
-    114: "\r",
-    116: "\t",
-}
-
-
-def read_string(source, start):
-    # type: (Source, int) -> Token
-    """Reads a string token from the source file.
-
-    "([^"\\\u000A\u000D\u2028\u2029]|(\\(u[0-9a-fA-F]{4}|["\\/bfnrt])))*"
-    """
-    body = source.body
-    body_length = len(body)
-
-    position = start + 1
-    chunk_start = position
-    code = 0  # type: Optional[int]
-    value = []  # type: List[str]
-    append = value.append
-
-    while position < body_length:
-        code = char_code_at(body, position)
-        if code in (
-            None,
-            # LineTerminator
-            0x000A,
-            0x000D,
-            # Quote
-            34,
-        ):
-            break
-
-        if code < 0x0020 and code != 0x0009:  # type: ignore
-            raise GraphQLSyntaxError(
-                source,
-                position,
-                u"Invalid character within String: {}.".format(print_char_code(code)),
-            )
-
-        position += 1
-        if code == 92:  # \
-            append(body[chunk_start : position - 1])
-
-            code = char_code_at(body, position)
-            escaped = ESCAPED_CHAR_CODES.get(code)  # type: ignore
-            if escaped is not None:
-                append(escaped)
-
-            elif code == 117:  # u
-                char_code = uni_char_code(
-                    char_code_at(body, position + 1) or 0,
-                    char_code_at(body, position + 2) or 0,
-                    char_code_at(body, position + 3) or 0,
-                    char_code_at(body, position + 4) or 0,
-                )
-
-                if char_code < 0:
-                    raise GraphQLSyntaxError(
-                        source,
-                        position,
-                        u"Invalid character escape sequence: \\u{}.".format(
-                            body[position + 1 : position + 5]
-                        ),
-                    )
-
-                append(unichr(char_code))
-                position += 4
-            else:
-                raise GraphQLSyntaxError(
-                    source,
-                    position,
-                    u"Invalid character escape sequence: \\{}.".format(
-                        unichr(code)  # type: ignore
-                    ),
-                )
-
-            position += 1
-            chunk_start = position
-
-    if code != 34:  # Quote (")
-        raise GraphQLSyntaxError(source, position, "Unterminated string")
-
-    append(body[chunk_start:position])
-    return Token(TokenKind.STRING, start, position + 1, u"".join(value))
-
-
-def uni_char_code(a, b, c, d):
-    # type: (int, int, int, int) -> int
-    """Converts four hexidecimal chars to the integer that the
-    string represents. For example, uniCharCode('0','0','0','f')
-    will return 15, and uniCharCode('0','0','f','f') returns 255.
-
-    Returns a negative number on error, if a char was invalid.
-
-    This is implemented by noting that char2hex() returns -1 on error,
-    which means the result of ORing the char2hex() will also be negative.
-    """
-    return char2hex(a) << 12 | char2hex(b) << 8 | char2hex(c) << 4 | char2hex(d)
-
-
-def char2hex(a):
-    # type: (int) -> int
-    """Converts a hex character to its integer value.
-    '0' becomes 0, '9' becomes 9
-    'A' becomes 10, 'F' becomes 15
-    'a' becomes 10, 'f' becomes 15
-
-    Returns -1 on error."""
-    if 48 <= a <= 57:  # 0-9
-        return a - 48
-    elif 65 <= a <= 70:  # A-F
-        return a - 55
-    elif 97 <= a <= 102:  # a-f
-        return a - 87
-    return -1
-
-
-def read_name(source, position):
-    # type: (Source, int) -> Token
-    """Reads an alphanumeric + underscore name from the source.
-
-    [_A-Za-z][_0-9A-Za-z]*"""
-    body = source.body
-    body_length = len(body)
-    end = position + 1
-
-    while end != body_length:
-        code = char_code_at(body, end)
-        if not (
-            code is not None
-            and (
-                code == 95
-                or 48 <= code <= 57  # _
-                or 65 <= code <= 90  # 0-9
-                or 97 <= code <= 122  # A-Z  # a-z
-            )
-        ):
-            break
-
-        end += 1
-
-    return Token(TokenKind.NAME, position, end, body[position:end])
diff --git a/graphql/language/location.py b/graphql/language/location.py
deleted file mode 100644
index 71c19bd..0000000
--- a/graphql/language/location.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from .source import Source
-    from typing import Any
-
-__all__ = ["get_location", "SourceLocation"]
-
-
-class SourceLocation(object):
-    __slots__ = "line", "column"
-
-    def __init__(self, line, column):
-        # type: (int, int) -> None
-        self.line = line
-        self.column = column
-
-    def __repr__(self):
-        # type: () -> str
-        return "SourceLocation(line={}, column={})".format(self.line, self.column)
-
-    def __eq__(self, other):
-        # type: (Any) -> bool
-        return (
-            isinstance(other, SourceLocation)
-            and self.line == other.line
-            and self.column == other.column
-        )
-
-
-def get_location(source, position):
-    # type: (Source, int) -> SourceLocation
-    lines = source.body[:position].splitlines()
-    if lines:
-        line = len(lines)
-        column = len(lines[-1]) + 1
-    else:
-        line = 1
-        column = 1
-    return SourceLocation(line, column)
diff --git a/graphql/language/parser.py b/graphql/language/parser.py
deleted file mode 100644
index 6157185..0000000
--- a/graphql/language/parser.py
+++ /dev/null
@@ -1,879 +0,0 @@
-from six import string_types
-
-from . import ast
-from ..error import GraphQLSyntaxError
-from .lexer import Lexer, TokenKind, get_token_desc, get_token_kind_desc
-from .source import Source
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Dict, Union, Any, Optional, Callable, List
-    from .lexer import Token
-    from .ast import (
-        Document,
-        Name,
-        OperationDefinition,
-        VariableDefinition,
-        Variable,
-        SelectionSet,
-        Field,
-        FragmentSpread,
-        Argument,
-        InlineFragment,
-        FragmentDefinition,
-        IntValue,
-        StringValue,
-        BooleanValue,
-        ObjectValue,
-        ListValue,
-        ObjectField,
-        Directive,
-        NamedType,
-        NonNullType,
-        ListType,
-        SchemaDefinition,
-        OperationTypeDefinition,
-        ScalarTypeDefinition,
-        ObjectTypeDefinition,
-        FieldDefinition,
-        InputValueDefinition,
-        InterfaceTypeDefinition,
-        UnionTypeDefinition,
-        EnumTypeDefinition,
-        EnumValueDefinition,
-        InputObjectTypeDefinition,
-        TypeExtensionDefinition,
-        DirectiveDefinition,
-    )
-
-__all__ = ["parse"]
-
-
-def parse(source, **kwargs):
-    # type: (Union[Source, str], **Any) -> Document
-    """Given a GraphQL source, parses it into a Document."""
-    options = {"no_location": False, "no_source": False}
-    options.update(kwargs)
-
-    if isinstance(source, string_types):
-        source_obj = Source(source)  # type: Source
-    else:
-        source_obj = source  # type: ignore
-
-    parser = Parser(source_obj, options)
-    return parse_document(parser)
-
-
-def parse_value(source, **kwargs):
-    options = {"no_location": False, "no_source": False}
-    options.update(kwargs)
-    source_obj = source
-
-    if isinstance(source, string_types):
-        source_obj = Source(source)
-
-    parser = Parser(source_obj, options)
-    return parse_value_literal(parser, False)
-
-
-class Parser(object):
-    __slots__ = "lexer", "source", "options", "prev_end", "token"
-
-    def __init__(self, source, options):
-        # type: (Source, Dict[str, bool]) -> None
-        self.lexer = Lexer(source)
-        self.source = source
-        self.options = options
-        self.prev_end = 0
-        self.token = self.lexer.next_token()
-
-
-class Loc(object):
-    __slots__ = "start", "end", "source"
-
-    def __init__(self, start, end, source=None):
-        # type: (int, int, Union[Source, str]) -> None
-        self.start = start
-        self.end = end
-        self.source = source
-
-    def __repr__(self):
-        # type: () -> str
-        source = " source={}".format(self.source) if self.source else ""
-        return "<Loc start={} end={}{}>".format(self.start, self.end, source)
-
-    def __eq__(self, other):
-        return (
-            isinstance(other, Loc)
-            and self.start == other.start
-            and self.end == other.end
-            and self.source == other.source
-        )
-
-
-def loc(parser, start):
-    # type: (Parser, int) -> Optional[Loc]
-    """Returns a location object, used to identify the place in
-    the source that created a given parsed object."""
-    if parser.options["no_location"]:
-        return None
-
-    if parser.options["no_source"]:
-        return Loc(start, parser.prev_end)
-
-    return Loc(start, parser.prev_end, parser.source)
-
-
-def advance(parser):
-    # type: (Parser) -> None
-    """Moves the internal parser object to the next lexed token."""
-    prev_end = parser.token.end
-    parser.prev_end = prev_end
-    parser.token = parser.lexer.next_token(prev_end)
-
-
-def peek(parser, kind):
-    # type: (Parser, int) -> bool
-    """Determines if the next token is of a given kind"""
-    return parser.token.kind == kind
-
-
-def skip(parser, kind):
-    # type: (Parser, int) -> bool
-    """If the next token is of the given kind, return true after advancing
-    the parser. Otherwise, do not change the parser state
-    and throw an error."""
-    match = parser.token.kind == kind
-    if match:
-        advance(parser)
-
-    return match
-
-
-def expect(parser, kind):
-    # type: (Parser, int) -> Token
-    """If the next token is of the given kind, return that token after
-    advancing the parser. Otherwise, do not change the parser state and
-    return False."""
-    token = parser.token
-    if token.kind == kind:
-        advance(parser)
-        return token
-
-    raise GraphQLSyntaxError(
-        parser.source,
-        token.start,
-        u"Expected {}, found {}".format(
-            get_token_kind_desc(kind), get_token_desc(token)
-        ),
-    )
-
-
-def expect_keyword(parser, value):
-    # type: (Parser, str) -> Token
-    """If the next token is a keyword with the given value, return that
-    token after advancing the parser. Otherwise, do not change the parser
-    state and return False."""
-    token = parser.token
-    if token.kind == TokenKind.NAME and token.value == value:
-        advance(parser)
-        return token
-
-    raise GraphQLSyntaxError(
-        parser.source,
-        token.start,
-        u'Expected "{}", found {}'.format(value, get_token_desc(token)),
-    )
-
-
-def unexpected(parser, at_token=None):
-    # type: (Parser, Optional[Any]) -> GraphQLSyntaxError
-    """Helper function for creating an error when an unexpected lexed token
-    is encountered."""
-    token = at_token or parser.token
-    return GraphQLSyntaxError(
-        parser.source, token.start, u"Unexpected {}".format(get_token_desc(token))
-    )
-
-
-def any(parser, open_kind, parse_fn, close_kind):
-    # type: (Parser, int, Callable, int) -> Any
-    """Returns a possibly empty list of parse nodes, determined by
-    the parse_fn. This list begins with a lex token of openKind
-    and ends with a lex token of closeKind. Advances the parser
-    to the next lex token after the closing token."""
-    expect(parser, open_kind)
-    nodes = []
-    while not skip(parser, close_kind):
-        nodes.append(parse_fn(parser))
-
-    return nodes
-
-
-def many(parser, open_kind, parse_fn, close_kind):
-    # type: (Parser, int, Callable, int) -> Any
-    """Returns a non-empty list of parse nodes, determined by
-    the parse_fn. This list begins with a lex token of openKind
-    and ends with a lex token of closeKind. Advances the parser
-    to the next lex token after the closing token."""
-    expect(parser, open_kind)
-    nodes = [parse_fn(parser)]
-    while not skip(parser, close_kind):
-        nodes.append(parse_fn(parser))
-
-    return nodes
-
-
-def parse_name(parser):
-    # type: (Parser) -> Name
-    """Converts a name lex token into a name parse node."""
-    token = expect(parser, TokenKind.NAME)
-    return ast.Name(value=token.value, loc=loc(parser, token.start))  # type: ignore
-
-
-# Implements the parsing rules in the Document section.
-
-
-def parse_document(parser):
-    # type: (Parser) -> Document
-    start = parser.token.start
-    definitions = []
-    while True:
-        definitions.append(parse_definition(parser))
-
-        if skip(parser, TokenKind.EOF):
-            break
-
-    return ast.Document(definitions=definitions, loc=loc(parser, start))
-
-
-def parse_definition(parser):
-    # type: (Parser) -> Any
-    if peek(parser, TokenKind.BRACE_L):
-        return parse_operation_definition(parser)
-
-    if peek(parser, TokenKind.NAME):
-        name = parser.token.value
-
-        if name in ("query", "mutation", "subscription"):
-            return parse_operation_definition(parser)
-        elif name == "fragment":
-            return parse_fragment_definition(parser)
-        elif name in (
-            "schema",
-            "scalar",
-            "type",
-            "interface",
-            "union",
-            "enum",
-            "input",
-            "extend",
-            "directive",
-        ):
-            return parse_type_system_definition(parser)
-
-    raise unexpected(parser)
-
-
-# Implements the parsing rules in the Operations section.
-def parse_operation_definition(parser):
-    # type: (Parser) -> OperationDefinition
-    start = parser.token.start
-    if peek(parser, TokenKind.BRACE_L):
-        return ast.OperationDefinition(
-            operation="query",
-            name=None,
-            variable_definitions=None,
-            directives=[],
-            selection_set=parse_selection_set(parser),
-            loc=loc(parser, start),
-        )
-
-    operation = parse_operation_type(parser)
-
-    name = None
-    if peek(parser, TokenKind.NAME):
-        name = parse_name(parser)
-
-    return ast.OperationDefinition(
-        operation=operation,
-        name=name,
-        variable_definitions=parse_variable_definitions(parser),
-        directives=parse_directives(parser),
-        selection_set=parse_selection_set(parser),
-        loc=loc(parser, start),
-    )
-
-
-def parse_operation_type(parser):
-    # type: (Parser) -> str
-    operation_token = expect(parser, TokenKind.NAME)
-    operation = operation_token.value
-    if operation == "query":
-        return "query"
-    elif operation == "mutation":
-        return "mutation"
-    elif operation == "subscription":
-        return "subscription"
-
-    raise unexpected(parser, operation_token)
-
-
-def parse_variable_definitions(parser):
-    # type: (Parser) -> List[VariableDefinition]
-    if peek(parser, TokenKind.PAREN_L):
-        return many(
-            parser, TokenKind.PAREN_L, parse_variable_definition, TokenKind.PAREN_R
-        )
-
-    return []
-
-
-def parse_variable_definition(parser):
-    # type: (Parser) -> VariableDefinition
-    start = parser.token.start
-
-    return ast.VariableDefinition(
-        variable=parse_variable(parser),
-        type=expect(parser, TokenKind.COLON) and parse_type(parser),
-        default_value=parse_value_literal(parser, True)
-        if skip(parser, TokenKind.EQUALS)
-        else None,
-        loc=loc(parser, start),
-    )
-
-
-def parse_variable(parser):
-    # type: (Parser) -> Variable
-    start = parser.token.start
-    expect(parser, TokenKind.DOLLAR)
-
-    return ast.Variable(name=parse_name(parser), loc=loc(parser, start))
-
-
-def parse_selection_set(parser):
-    # type: (Parser) -> SelectionSet
-    start = parser.token.start
-    return ast.SelectionSet(
-        selections=many(parser, TokenKind.BRACE_L, parse_selection, TokenKind.BRACE_R),
-        loc=loc(parser, start),
-    )
-
-
-def parse_selection(parser):
-    # type: (Parser) -> Union[Field, FragmentSpread, InlineFragment]
-    if peek(parser, TokenKind.SPREAD):
-        return parse_fragment(parser)
-    else:
-        return parse_field(parser)
-
-
-def parse_field(parser):
-    # type: (Parser) -> Field
-    # Corresponds to both Field and Alias in the spec
-    start = parser.token.start
-
-    name_or_alias = parse_name(parser)
-    if skip(parser, TokenKind.COLON):
-        alias = name_or_alias
-        name = parse_name(parser)
-    else:
-        alias = None  # type: ignore
-        name = name_or_alias
-
-    return ast.Field(
-        alias=alias,
-        name=name,
-        arguments=parse_arguments(parser),
-        directives=parse_directives(parser),
-        selection_set=parse_selection_set(parser)
-        if peek(parser, TokenKind.BRACE_L)
-        else None,
-        loc=loc(parser, start),
-    )
-
-
-def parse_arguments(parser):
-    # type: (Parser) -> List[Argument]
-    if peek(parser, TokenKind.PAREN_L):
-        return many(parser, TokenKind.PAREN_L, parse_argument, TokenKind.PAREN_R)
-
-    return []
-
-
-def parse_argument(parser):
-    # type: (Parser) -> Argument
-    start = parser.token.start
-
-    return ast.Argument(
-        name=parse_name(parser),
-        value=expect(parser, TokenKind.COLON) and parse_value_literal(parser, False),
-        loc=loc(parser, start),
-    )
-
-
-# Implements the parsing rules in the Fragments section.
-
-
-def parse_fragment(parser):
-    # type: (Parser) -> Union[FragmentSpread, InlineFragment]
-    # Corresponds to both FragmentSpread and InlineFragment in the spec
-    start = parser.token.start
-    expect(parser, TokenKind.SPREAD)
-
-    if peek(parser, TokenKind.NAME) and parser.token.value != "on":
-        return ast.FragmentSpread(
-            name=parse_fragment_name(parser),
-            directives=parse_directives(parser),
-            loc=loc(parser, start),
-        )
-
-    type_condition = None
-    if parser.token.value == "on":
-        advance(parser)
-        type_condition = parse_named_type(parser)
-
-    return ast.InlineFragment(
-        type_condition=type_condition,
-        directives=parse_directives(parser),
-        selection_set=parse_selection_set(parser),
-        loc=loc(parser, start),
-    )
-
-
-def parse_fragment_definition(parser):
-    # type: (Parser) -> FragmentDefinition
-    start = parser.token.start
-    expect_keyword(parser, "fragment")
-
-    return ast.FragmentDefinition(
-        name=parse_fragment_name(parser),
-        type_condition=parse_named_type(parser)
-        if expect_keyword(parser, "on")
-        else None,
-        directives=parse_directives(parser),
-        selection_set=parse_selection_set(parser),
-        loc=loc(parser, start),
-    )
-
-
-def parse_fragment_name(parser):
-    # type: (Parser) -> Name
-    if parser.token.value == "on":
-        raise unexpected(parser)
-
-    return parse_name(parser)
-
-
-def parse_value_literal(parser, is_const):
-    # type: (Parser, bool) -> Any
-    token = parser.token
-    if token.kind == TokenKind.BRACKET_L:
-        return parse_list(parser, is_const)
-
-    elif token.kind == TokenKind.BRACE_L:
-        return parse_object(parser, is_const)
-
-    elif token.kind == TokenKind.INT:
-        advance(parser)
-        return ast.IntValue(  # type: ignore
-            value=token.value, loc=loc(parser, token.start)
-        )
-
-    elif token.kind == TokenKind.FLOAT:
-        advance(parser)
-        return ast.FloatValue(  # type: ignore
-            value=token.value, loc=loc(parser, token.start)
-        )
-
-    elif token.kind == TokenKind.STRING:
-        advance(parser)
-        return ast.StringValue(  # type: ignore
-            value=token.value, loc=loc(parser, token.start)
-        )
-
-    elif token.kind == TokenKind.NAME:
-        if token.value in ("true", "false"):
-            advance(parser)
-            return ast.BooleanValue(  # type: ignore
-                value=token.value == "true", loc=loc(parser, token.start)
-            )
-
-        if token.value != "null":
-            advance(parser)
-            return ast.EnumValue(  # type: ignore
-                value=token.value, loc=loc(parser, token.start)
-            )
-
-    elif token.kind == TokenKind.DOLLAR:
-        if not is_const:
-            return parse_variable(parser)
-
-    raise unexpected(parser)
-
-
-# Implements the parsing rules in the Values section.
-def parse_variable_value(parser):
-    # type: (Parser) -> Union[IntValue, StringValue, Variable]
-    return parse_value_literal(parser, False)
-
-
-def parse_const_value(parser):
-    # type: (Parser) -> Union[BooleanValue, ObjectValue, StringValue]
-    return parse_value_literal(parser, True)
-
-
-def parse_list(parser, is_const):
-    # type: (Parser, bool) -> ListValue
-    start = parser.token.start
-    item = parse_const_value if is_const else parse_variable_value
-
-    return ast.ListValue(
-        values=any(parser, TokenKind.BRACKET_L, item, TokenKind.BRACKET_R),
-        loc=loc(parser, start),
-    )
-
-
-def parse_object(parser, is_const):
-    # type: (Parser, bool) -> ObjectValue
-    start = parser.token.start
-    expect(parser, TokenKind.BRACE_L)
-    fields = []
-
-    while not skip(parser, TokenKind.BRACE_R):
-        fields.append(parse_object_field(parser, is_const))
-
-    return ast.ObjectValue(fields=fields, loc=loc(parser, start))
-
-
-def parse_object_field(parser, is_const):
-    # type: (Parser, bool) -> ObjectField
-    start = parser.token.start
-    return ast.ObjectField(
-        name=parse_name(parser),
-        value=expect(parser, TokenKind.COLON) and parse_value_literal(parser, is_const),
-        loc=loc(parser, start),
-    )
-
-
-# Implements the parsing rules in the Directives section.
-
-
-def parse_directives(parser):
-    # type: (Parser) -> List[Directive]
-    directives = []
-    while peek(parser, TokenKind.AT):
-        directives.append(parse_directive(parser))
-    return directives
-
-
-def parse_directive(parser):
-    # type: (Parser) -> Directive
-    start = parser.token.start
-    expect(parser, TokenKind.AT)
-
-    return ast.Directive(
-        name=parse_name(parser),
-        arguments=parse_arguments(parser),
-        loc=loc(parser, start),
-    )
-
-
-# Implements the parsing rules in the Types section.
-def parse_type(parser):
-    # type: (Parser) -> Union[NamedType, NonNullType, ListType]
-    """Handles the 'Type': TypeName, ListType, and NonNullType
-    parsing rules."""
-    start = parser.token.start
-    if skip(parser, TokenKind.BRACKET_L):
-        ast_type = parse_type(parser)
-        expect(parser, TokenKind.BRACKET_R)
-        ast_type = ast.ListType(type=ast_type, loc=loc(parser, start))  # type: ignore
-
-    else:
-        ast_type = parse_named_type(parser)
-
-    if skip(parser, TokenKind.BANG):
-        return ast.NonNullType(type=ast_type, loc=loc(parser, start))
-
-    return ast_type
-
-
-def parse_named_type(parser):
-    # type: (Parser) -> NamedType
-    start = parser.token.start
-    return ast.NamedType(name=parse_name(parser), loc=loc(parser, start))
-
-
-def parse_type_system_definition(parser):
-    # type: (Parser) -> Any
-    """
-      TypeSystemDefinition :
-        - SchemaDefinition
-        - TypeDefinition
-        - TypeExtensionDefinition
-        - DirectiveDefinition
-
-      TypeDefinition :
-      - ScalarTypeDefinition
-      - ObjectTypeDefinition
-      - InterfaceTypeDefinition
-      - UnionTypeDefinition
-      - EnumTypeDefinition
-      - InputObjectTypeDefinition
-    """
-    if not peek(parser, TokenKind.NAME):
-        raise unexpected(parser)
-
-    name = parser.token.value
-
-    if name == "schema":
-        return parse_schema_definition(parser)
-
-    elif name == "scalar":
-        return parse_scalar_type_definition(parser)
-
-    elif name == "type":
-        return parse_object_type_definition(parser)
-
-    elif name == "interface":
-        return parse_interface_type_definition(parser)
-
-    elif name == "union":
-        return parse_union_type_definition(parser)
-
-    elif name == "enum":
-        return parse_enum_type_definition(parser)
-
-    elif name == "input":
-        return parse_input_object_type_definition(parser)
-
-    elif name == "extend":
-        return parse_type_extension_definition(parser)
-
-    elif name == "directive":
-        return parse_directive_definition(parser)
-
-    raise unexpected(parser)
-
-
-def parse_schema_definition(parser):
-    # type: (Parser) -> SchemaDefinition
-    start = parser.token.start
-    expect_keyword(parser, "schema")
-    directives = parse_directives(parser)
-    operation_types = many(
-        parser, TokenKind.BRACE_L, parse_operation_type_definition, TokenKind.BRACE_R
-    )
-
-    return ast.SchemaDefinition(
-        directives=directives, operation_types=operation_types, loc=loc(parser, start)
-    )
-
-
-def parse_operation_type_definition(parser):
-    # type: (Parser) -> OperationTypeDefinition
-    start = parser.token.start
-    operation = parse_operation_type(parser)
-    expect(parser, TokenKind.COLON)
-
-    return ast.OperationTypeDefinition(
-        operation=operation, type=parse_named_type(parser), loc=loc(parser, start)
-    )
-
-
-def parse_scalar_type_definition(parser):
-    # type: (Parser) -> ScalarTypeDefinition
-    start = parser.token.start
-    expect_keyword(parser, "scalar")
-
-    return ast.ScalarTypeDefinition(
-        name=parse_name(parser),
-        directives=parse_directives(parser),
-        loc=loc(parser, start),
-    )
-
-
-def parse_object_type_definition(parser):
-    # type: (Parser) -> ObjectTypeDefinition
-    start = parser.token.start
-    expect_keyword(parser, "type")
-    return ast.ObjectTypeDefinition(
-        name=parse_name(parser),
-        interfaces=parse_implements_interfaces(parser),
-        directives=parse_directives(parser),
-        fields=any(
-            parser, TokenKind.BRACE_L, parse_field_definition, TokenKind.BRACE_R
-        ),
-        loc=loc(parser, start),
-    )
-
-
-def parse_implements_interfaces(parser):
-    # type: (Parser) -> List[NamedType]
-    types = []
-    if parser.token.value == "implements":
-        advance(parser)
-
-        while True:
-            types.append(parse_named_type(parser))
-
-            if not peek(parser, TokenKind.NAME):
-                break
-
-    return types
-
-
-def parse_field_definition(parser):
-    # type: (Parser) -> FieldDefinition
-    start = parser.token.start
-
-    return ast.FieldDefinition(  # type: ignore
-        name=parse_name(parser),
-        arguments=parse_argument_defs(parser),
-        type=expect(parser, TokenKind.COLON) and parse_type(parser),
-        directives=parse_directives(parser),
-        loc=loc(parser, start),
-    )
-
-
-def parse_argument_defs(parser):
-    # type: (Parser) -> List[InputValueDefinition]
-    if not peek(parser, TokenKind.PAREN_L):
-        return []
-
-    return many(parser, TokenKind.PAREN_L, parse_input_value_def, TokenKind.PAREN_R)
-
-
-def parse_input_value_def(parser):
-    # type: (Parser) -> InputValueDefinition
-    start = parser.token.start
-
-    return ast.InputValueDefinition(  # type: ignore
-        name=parse_name(parser),
-        type=expect(parser, TokenKind.COLON) and parse_type(parser),
-        default_value=parse_const_value(parser)
-        if skip(parser, TokenKind.EQUALS)
-        else None,
-        directives=parse_directives(parser),
-        loc=loc(parser, start),
-    )
-
-
-def parse_interface_type_definition(parser):
-    # type: (Parser) -> InterfaceTypeDefinition
-    start = parser.token.start
-    expect_keyword(parser, "interface")
-
-    return ast.InterfaceTypeDefinition(
-        name=parse_name(parser),
-        directives=parse_directives(parser),
-        fields=any(
-            parser, TokenKind.BRACE_L, parse_field_definition, TokenKind.BRACE_R
-        ),
-        loc=loc(parser, start),
-    )
-
-
-def parse_union_type_definition(parser):
-    # type: (Parser) -> UnionTypeDefinition
-    start = parser.token.start
-    expect_keyword(parser, "union")
-
-    return ast.UnionTypeDefinition(  # type: ignore
-        name=parse_name(parser),
-        directives=parse_directives(parser),
-        types=expect(parser, TokenKind.EQUALS) and parse_union_members(parser),
-        loc=loc(parser, start),
-    )
-
-
-def parse_union_members(parser):
-    # type: (Parser) -> List[NamedType]
-    members = []
-
-    while True:
-        members.append(parse_named_type(parser))
-
-        if not skip(parser, TokenKind.PIPE):
-            break
-
-    return members
-
-
-def parse_enum_type_definition(parser):
-    # type: (Parser) -> EnumTypeDefinition
-    start = parser.token.start
-    expect_keyword(parser, "enum")
-
-    return ast.EnumTypeDefinition(
-        name=parse_name(parser),
-        directives=parse_directives(parser),
-        values=many(
-            parser, TokenKind.BRACE_L, parse_enum_value_definition, TokenKind.BRACE_R
-        ),
-        loc=loc(parser, start),
-    )
-
-
-def parse_enum_value_definition(parser):
-    # type: (Parser) -> EnumValueDefinition
-    start = parser.token.start
-
-    return ast.EnumValueDefinition(
-        name=parse_name(parser),
-        directives=parse_directives(parser),
-        loc=loc(parser, start),
-    )
-
-
-def parse_input_object_type_definition(parser):
-    # type: (Parser) -> InputObjectTypeDefinition
-    start = parser.token.start
-    expect_keyword(parser, "input")
-
-    return ast.InputObjectTypeDefinition(
-        name=parse_name(parser),
-        directives=parse_directives(parser),
-        fields=any(parser, TokenKind.BRACE_L, parse_input_value_def, TokenKind.BRACE_R),
-        loc=loc(parser, start),
-    )
-
-
-def parse_type_extension_definition(parser):
-    # type: (Parser) -> TypeExtensionDefinition
-    start = parser.token.start
-    expect_keyword(parser, "extend")
-
-    return ast.TypeExtensionDefinition(
-        definition=parse_object_type_definition(parser), loc=loc(parser, start)
-    )
-
-
-def parse_directive_definition(parser):
-    # type: (Parser) -> DirectiveDefinition
-    start = parser.token.start
-    expect_keyword(parser, "directive")
-    expect(parser, TokenKind.AT)
-
-    name = parse_name(parser)
-    args = parse_argument_defs(parser)
-    expect_keyword(parser, "on")
-
-    locations = parse_directive_locations(parser)
-    return ast.DirectiveDefinition(
-        name=name, locations=locations, arguments=args, loc=loc(parser, start)
-    )
-
-
-def parse_directive_locations(parser):
-    # type: (Parser) -> List[Name]
-    locations = []
-
-    while True:
-        locations.append(parse_name(parser))
-
-        if not skip(parser, TokenKind.PIPE):
-            break
-
-    return locations
diff --git a/graphql/language/printer.py b/graphql/language/printer.py
deleted file mode 100644
index e540bce..0000000
--- a/graphql/language/printer.py
+++ /dev/null
@@ -1,282 +0,0 @@
-import json
-
-from .visitor import Visitor, visit
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, List, Optional
-    from graphql.language.ast import Node
-
-__all__ = ["print_ast"]
-
-
-def print_ast(ast):
-    # type: (Node) -> str
-    return visit(ast, PrintingVisitor())
-
-
-class PrintingVisitor(Visitor):
-    __slots__ = ()
-
-    def leave_Name(self, node, *args):
-        # type: (Any, *Any) -> str
-        return node.value  # type: ignore
-
-    def leave_Variable(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "$" + node.name  # type: ignore
-
-    def leave_Document(self, node, *args):
-        # type: (Any, *Any) -> str
-        return join(node.definitions, "\n\n") + "\n"  # type: ignore
-
-    def leave_OperationDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        name = node.name
-        selection_set = node.selection_set
-        op = node.operation
-        var_defs = wrap("(", join(node.variable_definitions, ", "), ")")
-        directives = join(node.directives, " ")
-
-        if not name and not directives and not var_defs and op == "query":
-            return selection_set
-
-        return join([op, join([name, var_defs]), directives, selection_set], " ")
-
-    def leave_VariableDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return node.variable + ": " + node.type + wrap(" = ", node.default_value)
-
-    def leave_SelectionSet(self, node, *args):
-        # type: (Any, *Any) -> str
-        return block(node.selections)
-
-    def leave_Field(self, node, *args):
-        # type: (Any, *Any) -> str
-        return join(
-            [
-                wrap("", node.alias, ": ")
-                + node.name
-                + wrap("(", join(node.arguments, ", "), ")"),
-                join(node.directives, " "),
-                node.selection_set,
-            ],
-            " ",
-        )
-
-    def leave_Argument(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "{0.name}: {0.value}".format(node)
-
-    # Fragments
-
-    def leave_FragmentSpread(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "..." + node.name + wrap(" ", join(node.directives, " "))
-
-    def leave_InlineFragment(self, node, *args):
-        # type: (Any, *Any) -> str
-        return join(
-            [
-                "...",
-                wrap("on ", node.type_condition),
-                join(node.directives, ""),
-                node.selection_set,
-            ],
-            " ",
-        )
-
-    def leave_FragmentDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return (
-            "fragment {} on {} ".format(node.name, node.type_condition)
-            + wrap("", join(node.directives, " "), " ")
-            + node.selection_set
-        )
-
-    # Value
-
-    def leave_IntValue(self, node, *args):
-        # type: (Any, *Any) -> str
-        return node.value
-
-    def leave_FloatValue(self, node, *args):
-        return node.value
-
-    def leave_StringValue(self, node, *args):
-        # type: (Any, *Any) -> str
-        return json.dumps(node.value)
-
-    def leave_BooleanValue(self, node, *args):
-        # type: (Any, *Any) -> str
-        return json.dumps(node.value)
-
-    def leave_EnumValue(self, node, *args):
-        # type: (Any, *Any) -> str
-        return node.value
-
-    def leave_ListValue(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "[" + join(node.values, ", ") + "]"
-
-    def leave_ObjectValue(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "{" + join(node.fields, ", ") + "}"
-
-    def leave_ObjectField(self, node, *args):
-        # type: (Any, *Any) -> str
-        return node.name + ": " + node.value
-
-    # Directive
-
-    def leave_Directive(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "@" + node.name + wrap("(", join(node.arguments, ", "), ")")
-
-    # Type
-
-    def leave_NamedType(self, node, *args):
-        # type: (Any, *Any) -> str
-        return node.name
-
-    def leave_ListType(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "[" + node.type + "]"
-
-    def leave_NonNullType(self, node, *args):
-        # type: (Any, *Any) -> str
-        return node.type + "!"
-
-    # Type Definitions:
-
-    def leave_SchemaDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return join(
-            ["schema", join(node.directives, " "), block(node.operation_types)], " "
-        )
-
-    def leave_OperationTypeDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "{}: {}".format(node.operation, node.type)
-
-    def leave_ScalarTypeDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "scalar " + node.name + wrap(" ", join(node.directives, " "))
-
-    def leave_ObjectTypeDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return join(
-            [
-                "type",
-                node.name,
-                wrap("implements ", join(node.interfaces, ", ")),
-                join(node.directives, " "),
-                block(node.fields),
-            ],
-            " ",
-        )
-
-    def leave_FieldDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return (
-            node.name
-            + wrap("(", join(node.arguments, ", "), ")")
-            + ": "
-            + node.type
-            + wrap(" ", join(node.directives, " "))
-        )
-
-    def leave_InputValueDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return (
-            node.name
-            + ": "
-            + node.type
-            + wrap(" = ", node.default_value)
-            + wrap(" ", join(node.directives, " "))
-        )
-
-    def leave_InterfaceTypeDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return (
-            "interface "
-            + node.name
-            + wrap(" ", join(node.directives, " "))
-            + " "
-            + block(node.fields)
-        )
-
-    def leave_UnionTypeDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return (
-            "union "
-            + node.name
-            + wrap(" ", join(node.directives, " "))
-            + " = "
-            + join(node.types, " | ")
-        )
-
-    def leave_EnumTypeDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return (
-            "enum "
-            + node.name
-            + wrap(" ", join(node.directives, " "))
-            + " "
-            + block(node.values)
-        )
-
-    def leave_EnumValueDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return node.name + wrap(" ", join(node.directives, " "))
-
-    def leave_InputObjectTypeDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return (
-            "input "
-            + node.name
-            + wrap(" ", join(node.directives, " "))
-            + " "
-            + block(node.fields)
-        )
-
-    def leave_TypeExtensionDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "extend " + node.definition
-
-    def leave_DirectiveDefinition(self, node, *args):
-        # type: (Any, *Any) -> str
-        return "directive @{}{} on {}".format(
-            node.name,
-            wrap("(", join(node.arguments, ", "), ")"),
-            " | ".join(node.locations),
-        )
-
-
-def join(maybe_list, separator=""):
-    # type: (Optional[List[str]], str) -> str
-    if maybe_list:
-        return separator.join(filter(None, maybe_list))
-    return ""
-
-
-def block(_list):
-    # type: (List[str]) -> str
-    """Given a list, print each item on its own line, wrapped in an indented "{ }" block."""
-    if _list:
-        return indent("{\n" + join(_list, "\n")) + "\n}"
-    return "{}"
-
-
-def wrap(start, maybe_str, end=""):
-    # type: (str, Optional[str], str) -> str
-    if maybe_str:
-        return start + maybe_str + end
-    return ""
-
-
-def indent(maybe_str):
-    # type: (Optional[str]) -> str
-    if maybe_str:
-        return maybe_str.replace("\n", "\n  ")
-    return ""
diff --git a/graphql/language/source.py b/graphql/language/source.py
deleted file mode 100644
index 0f73777..0000000
--- a/graphql/language/source.py
+++ /dev/null
@@ -1,17 +0,0 @@
-__all__ = ["Source"]
-
-
-class Source(object):
-    __slots__ = "body", "name"
-
-    def __init__(self, body, name="GraphQL"):
-        # type: (str, str) -> None
-        self.body = body
-        self.name = name
-
-    def __eq__(self, other):
-        return self is other or (
-            isinstance(other, Source)
-            and self.body == other.body
-            and self.name == other.name
-        )
diff --git a/graphql/language/tests/__init__.py b/graphql/language/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/language/tests/fixtures.py b/graphql/language/tests/fixtures.py
deleted file mode 100644
index b16653c..0000000
--- a/graphql/language/tests/fixtures.py
+++ /dev/null
@@ -1,135 +0,0 @@
-KITCHEN_SINK = """
-# Copyright (c) 2015, Facebook, Inc.
-# All rights reserved.
-#
-# This source code is licensed under the BSD-style license found in the
-# LICENSE file in the root directory of this source tree. An additional grant
-# of patent rights can be found in the PATENTS file in the same directory.
-
-query queryName($foo: ComplexType, $site: Site = MOBILE) {
-  whoever123is: node(id: [123, 456]) {
-    id ,
-    ... on User @defer {
-      field2 {
-        id ,
-        alias: field1(first:10, after:$foo,) @include(if: $foo) {
-          id,
-          ...frag
-        }
-      }
-    }
-    ... @skip(unless: $foo) {
-      id
-    }
-    ... {
-      id
-    }
-  }
-}
-
-mutation likeStory {
-  like(story: 123) @defer {
-    story {
-      id
-    }
-  }
-}
-
-subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
-  storyLikeSubscribe(input: $input) {
-    story {
-      likers {
-        count
-      }
-      likeSentence {
-        text
-      }
-    }
-  }
-}
-
-fragment frag on Friend {
-  foo(size: $size, bar: $b, obj: {key: "value"})
-}
-
-{
-  unnamed(truthy: true, falsey: false),
-  query
-}
-"""
-
-SCHEMA_KITCHEN_SINK = """
-
-# Copyright (c) 2015, Facebook, Inc.
-# All rights reserved.
-#
-# This source code is licensed under the BSD-style license found in the
-# LICENSE file in the root directory of this source tree. An additional grant
-# of patent rights can be found in the PATENTS file in the same directory.
-
-schema {
-  query: QueryType
-  mutation: MutationType
-}
-
-type Foo implements Bar {
-  one: Type
-  two(argument: InputType!): Type
-  three(argument: InputType, other: String): Int
-  four(argument: String = "string"): String
-  five(argument: [String] = ["string", "string"]): String
-  six(argument: InputType = {key: "value"}): Type
-}
-
-type AnnotatedObject @onObject(arg: "value") {
-  annotatedField(arg: Type = "default" @onArg): Type @onField
-}
-
-interface Bar {
-  one: Type
-  four(argument: String = "string"): String
-}
-
-interface AnnotatedInterface @onInterface {
-  annotatedField(arg: Type @onArg): Type @onField
-}
-
-union Feed = Story | Article | Advert
-
-union AnnotatedUnion @onUnion = A | B
-
-scalar CustomScalar
-
-scalar AnnotatedScalar @onScalar
-
-enum Site {
-  DESKTOP
-  MOBILE
-}
-
-enum AnnotatedEnum @onEnum {
-  ANNOTATED_VALUE @onEnumValue
-  OTHER_VALUE
-}
-
-input InputType {
-  key: String!
-  answer: Int = 42
-}
-
-input AnnotatedInput @onInputObjectType {
-  annotatedField: Type @onField
-}
-
-extend type Foo {
-  seven(argument: [String]): Type
-}
-
-extend type Foo @onType {}
-
-type NoFields {}
-
-directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
-
-directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
-"""
diff --git a/graphql/language/tests/test_ast.py b/graphql/language/tests/test_ast.py
deleted file mode 100644
index 64b849a..0000000
--- a/graphql/language/tests/test_ast.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import copy
-
-from graphql.language.visitor_meta import QUERY_DOCUMENT_KEYS
-
-
-def test_ast_is_hashable():
-    # type: () -> None
-    for node_class in QUERY_DOCUMENT_KEYS:
-        node = node_class(loc=None, **{k: k for k in node_class._fields})
-        assert hash(node)
-
-
-def test_ast_is_copyable():
-    # type: () -> None
-    for node_class in QUERY_DOCUMENT_KEYS:
-        node = node_class(loc=None, **{k: k for k in node_class._fields})
-        assert copy.copy(node) == node
-
-
-def test_ast_is_reprable():
-    # type: () -> None
-    for node_class in QUERY_DOCUMENT_KEYS:
-        node = node_class(loc=None, **{k: k for k in node_class._fields})
-        assert repr(node)
diff --git a/graphql/language/tests/test_lexer.py b/graphql/language/tests/test_lexer.py
deleted file mode 100644
index dda777d..0000000
--- a/graphql/language/tests/test_lexer.py
+++ /dev/null
@@ -1,318 +0,0 @@
-from pytest import raises
-
-from graphql.error import GraphQLSyntaxError
-from graphql.language.lexer import Lexer, Token, TokenKind
-from graphql.language.source import Source
-
-
-def lex_one(s):
-    # type: (str) -> Token
-    return Lexer(Source(s)).next_token()
-
-
-def test_repr_token():
-    # type: () -> None
-    token = lex_one("500")
-    assert repr(token) == "<Token kind=Int at 0..3 value='500'>"
-
-
-def test_disallows_uncommon_control_characters():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"\u0007")
-
-    assert (
-        u'Syntax Error GraphQL (1:1) Invalid character "\\u0007"'
-        in excinfo.value.message
-    )
-
-
-def test_accepts_bom_header():
-    # type: () -> None
-    assert lex_one(u"\uFEFF foo") == Token(TokenKind.NAME, 2, 5, u"foo")
-
-
-def test_skips_whitespace():
-    # type: () -> None
-    assert (
-        lex_one(
-            u"""
-
-    foo
-
-
-"""
-        )
-        == Token(TokenKind.NAME, 6, 9, "foo")
-    )
-
-    assert (
-        lex_one(
-            u"""
-    #comment
-    foo#comment
-"""
-        )
-        == Token(TokenKind.NAME, 18, 21, "foo")
-    )
-
-    assert lex_one(u""",,,foo,,,""") == Token(TokenKind.NAME, 3, 6, "foo")
-
-
-def test_errors_respect_whitespace():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(
-            u"""
-
-    ?
-
-
-"""
-        )
-    assert excinfo.value.message == (
-        u'Syntax Error GraphQL (3:5) Unexpected character "?".\n'
-        u"\n"
-        u"2: \n"
-        u"3:     ?\n"
-        u"       ^\n"
-        u"4: \n"
-    )
-
-
-def test_lexes_strings():
-    # type: () -> None
-    assert lex_one(u'"simple"') == Token(TokenKind.STRING, 0, 8, "simple")
-    assert lex_one(u'" white space "') == Token(
-        TokenKind.STRING, 0, 15, " white space "
-    )
-    assert lex_one(u'"quote \\""') == Token(TokenKind.STRING, 0, 10, 'quote "')
-    assert lex_one(u'"escaped \\n\\r\\b\\t\\f"') == Token(
-        TokenKind.STRING, 0, 20, "escaped \n\r\b\t\f"
-    )
-    assert lex_one(u'"slashes \\\\ \\/"') == Token(
-        TokenKind.STRING, 0, 15, "slashes \\ /"
-    )
-    assert lex_one(u'"unicode \\u1234\\u5678\\u90AB\\uCDEF"') == Token(
-        TokenKind.STRING, 0, 34, u"unicode \u1234\u5678\u90AB\uCDEF"
-    )
-
-
-def test_lex_reports_useful_string_errors():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"')
-    assert u"Syntax Error GraphQL (1:2) Unterminated string" in excinfo.value.message
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"no end quote')
-    assert u"Syntax Error GraphQL (1:14) Unterminated string" in excinfo.value.message
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"contains unescaped \u0007 control char"')
-    assert (
-        u'Syntax Error GraphQL (1:21) Invalid character within String: "\\u0007".'
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"null-byte is not \u0000 end of file"')
-    assert (
-        u'Syntax Error GraphQL (1:19) Invalid character within String: "\\u0000".'
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"multi\nline"')
-    assert u"Syntax Error GraphQL (1:7) Unterminated string" in excinfo.value.message
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"multi\rline"')
-    assert u"Syntax Error GraphQL (1:7) Unterminated string" in excinfo.value.message
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"bad \\z esc"')
-    assert (
-        u"Syntax Error GraphQL (1:7) Invalid character escape sequence: \\z."
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"bad \\x esc"')
-    assert (
-        u"Syntax Error GraphQL (1:7) Invalid character escape sequence: \\x."
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"bad \\u1 esc"')
-    assert (
-        u"Syntax Error GraphQL (1:7) Invalid character escape sequence: \\u1 es."
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"bad \\u0XX1 esc"')
-    assert (
-        u"Syntax Error GraphQL (1:7) Invalid character escape sequence: \\u0XX1."
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"bad \\uXXXX esc"')
-    assert (
-        u"Syntax Error GraphQL (1:7) Invalid character escape sequence: \\uXXXX"
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"bad \\uFXXX esc"')
-    assert (
-        u"Syntax Error GraphQL (1:7) Invalid character escape sequence: \\uFXXX."
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u'"bad \\uXXXF esc"')
-    assert (
-        u"Syntax Error GraphQL (1:7) Invalid character escape sequence: \\uXXXF."
-        in excinfo.value.message
-    )
-
-
-def test_lexes_numbers():
-    # type: () -> None
-    assert lex_one(u"4") == Token(TokenKind.INT, 0, 1, "4")
-    assert lex_one(u"4.123") == Token(TokenKind.FLOAT, 0, 5, "4.123")
-    assert lex_one(u"-4") == Token(TokenKind.INT, 0, 2, "-4")
-    assert lex_one(u"9") == Token(TokenKind.INT, 0, 1, "9")
-    assert lex_one(u"0") == Token(TokenKind.INT, 0, 1, "0")
-    assert lex_one(u"-4.123") == Token(TokenKind.FLOAT, 0, 6, "-4.123")
-    assert lex_one(u"0.123") == Token(TokenKind.FLOAT, 0, 5, "0.123")
-    assert lex_one(u"123e4") == Token(TokenKind.FLOAT, 0, 5, "123e4")
-    assert lex_one(u"123E4") == Token(TokenKind.FLOAT, 0, 5, "123E4")
-    assert lex_one(u"123e-4") == Token(TokenKind.FLOAT, 0, 6, "123e-4")
-    assert lex_one(u"123e+4") == Token(TokenKind.FLOAT, 0, 6, "123e+4")
-    assert lex_one(u"-1.123e4") == Token(TokenKind.FLOAT, 0, 8, "-1.123e4")
-    assert lex_one(u"-1.123E4") == Token(TokenKind.FLOAT, 0, 8, "-1.123E4")
-    assert lex_one(u"-1.123e-4") == Token(TokenKind.FLOAT, 0, 9, "-1.123e-4")
-    assert lex_one(u"-1.123e+4") == Token(TokenKind.FLOAT, 0, 9, "-1.123e+4")
-    assert lex_one(u"-1.123e4567") == Token(TokenKind.FLOAT, 0, 11, "-1.123e4567")
-
-
-def test_lex_reports_useful_number_errors():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"00")
-    assert (
-        u'Syntax Error GraphQL (1:2) Invalid number, unexpected digit after 0: "0".'
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"+1")
-    assert (
-        u'Syntax Error GraphQL (1:1) Unexpected character "+"' in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"1.")
-    assert (
-        u"Syntax Error GraphQL (1:3) Invalid number, expected digit but got: <EOF>."
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u".123")
-    assert (
-        u'Syntax Error GraphQL (1:1) Unexpected character ".".' in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"1.A")
-    assert (
-        u'Syntax Error GraphQL (1:3) Invalid number, expected digit but got: "A".'
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"-A")
-    assert (
-        u'Syntax Error GraphQL (1:2) Invalid number, expected digit but got: "A".'
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"1.0e")
-    assert (
-        u"Syntax Error GraphQL (1:5) Invalid number, expected digit but got: <EOF>."
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"1.0eA")
-    assert (
-        u'Syntax Error GraphQL (1:5) Invalid number, expected digit but got: "A".'
-        in excinfo.value.message
-    )
-
-
-def test_lexes_punctuation():
-    # type: () -> None
-    assert lex_one(u"!") == Token(TokenKind.BANG, 0, 1)
-    assert lex_one(u"$") == Token(TokenKind.DOLLAR, 0, 1)
-    assert lex_one(u"(") == Token(TokenKind.PAREN_L, 0, 1)
-    assert lex_one(u")") == Token(TokenKind.PAREN_R, 0, 1)
-    assert lex_one(u"...") == Token(TokenKind.SPREAD, 0, 3)
-    assert lex_one(u":") == Token(TokenKind.COLON, 0, 1)
-    assert lex_one(u"=") == Token(TokenKind.EQUALS, 0, 1)
-    assert lex_one(u"@") == Token(TokenKind.AT, 0, 1)
-    assert lex_one(u"[") == Token(TokenKind.BRACKET_L, 0, 1)
-    assert lex_one(u"]") == Token(TokenKind.BRACKET_R, 0, 1)
-    assert lex_one(u"{") == Token(TokenKind.BRACE_L, 0, 1)
-    assert lex_one(u"|") == Token(TokenKind.PIPE, 0, 1)
-    assert lex_one(u"}") == Token(TokenKind.BRACE_R, 0, 1)
-
-
-def test_lex_reports_useful_unknown_character_error():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"..")
-    assert (
-        u'Syntax Error GraphQL (1:1) Unexpected character "."' in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"?")
-    assert (
-        u'Syntax Error GraphQL (1:1) Unexpected character "?"' in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"\u203B")
-    assert (
-        u'Syntax Error GraphQL (1:1) Unexpected character "\\u203B"'
-        in excinfo.value.message
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        lex_one(u"\u200b")
-    assert (
-        u'Syntax Error GraphQL (1:1) Unexpected character "\\u200B"'
-        in excinfo.value.message
-    )
-
-
-def test_lex_reports_useful_information_for_dashes_in_names():
-    # type: () -> None
-    q = u"a-b"
-    lexer = Lexer(Source(q))
-    first_token = lexer.next_token()
-    assert first_token == Token(TokenKind.NAME, 0, 1, "a")
-    with raises(GraphQLSyntaxError) as excinfo:
-        lexer.next_token()
-
-    assert (
-        u'Syntax Error GraphQL (1:3) Invalid number, expected digit but got: "b".'
-        in excinfo.value.message
-    )
diff --git a/graphql/language/tests/test_location.py b/graphql/language/tests/test_location.py
deleted file mode 100644
index 3562d53..0000000
--- a/graphql/language/tests/test_location.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from graphql.language.location import SourceLocation
-
-
-def test_repr_source_location():
-    # type: () -> None
-    loc = SourceLocation(10, 25)
-    assert repr(loc) == "SourceLocation(line=10, column=25)"
diff --git a/graphql/language/tests/test_parser.py b/graphql/language/tests/test_parser.py
deleted file mode 100644
index 6c9d5f4..0000000
--- a/graphql/language/tests/test_parser.py
+++ /dev/null
@@ -1,307 +0,0 @@
-from pytest import raises
-
-from graphql.error import GraphQLSyntaxError
-from graphql.language import ast
-from graphql.language.location import SourceLocation
-from graphql.language.parser import Loc, parse
-from graphql.language.source import Source
-
-from .fixtures import KITCHEN_SINK
-
-
-def test_repr_loc():
-    # type: () -> None
-    loc = Loc(start=10, end=25, source="foo")
-    assert repr(loc) == "<Loc start=10 end=25 source=foo>"
-
-
-def test_empty_parse():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("")
-    assert (
-        u"Syntax Error GraphQL (1:1) Unexpected EOF\n" u"\n"
-    ) == excinfo.value.message
-
-
-def test_parse_provides_useful_errors():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("""{""")
-    assert (
-        u"Syntax Error GraphQL (1:2) Expected Name, found EOF\n"
-        u"\n"
-        u"1: {\n"
-        u"    ^\n"
-        u""
-    ) == excinfo.value.message
-
-    assert excinfo.value.positions == [1]
-    assert excinfo.value.locations == [SourceLocation(line=1, column=2)]
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse(
-            """{ ...MissingOn }
-fragment MissingOn Type
-"""
-        )
-    assert 'Syntax Error GraphQL (2:20) Expected "on", found Name "Type"' in str(
-        excinfo.value
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("{ field: {} }")
-    assert "Syntax Error GraphQL (1:10) Expected Name, found {" in str(excinfo.value)
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("notanoperation Foo { field }")
-    assert 'Syntax Error GraphQL (1:1) Unexpected Name "notanoperation"' in str(
-        excinfo.value
-    )
-
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("...")
-    assert "Syntax Error GraphQL (1:1) Unexpected ..." in str(excinfo.value)
-
-
-def test_parse_provides_useful_error_when_using_source():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse(Source("query", "MyQuery.graphql"))
-    assert "Syntax Error MyQuery.graphql (1:6) Expected {, found EOF" in str(
-        excinfo.value
-    )
-
-
-def test_parses_variable_inline_values():
-    # type: () -> None
-    parse("{ field(complex: { a: { b: [ $var ] } }) }")
-
-
-def test_parses_constant_default_values():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("query Foo($x: Complex = { a: { b: [ $var ] } }) { field }")
-    assert "Syntax Error GraphQL (1:37) Unexpected $" in str(excinfo.value)
-
-
-def test_does_not_accept_fragments_named_on():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("fragment on on on { on }")
-
-    assert 'Syntax Error GraphQL (1:10) Unexpected Name "on"' in excinfo.value.message
-
-
-def test_does_not_accept_fragments_spread_of_on():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("{ ...on }")
-
-    assert "Syntax Error GraphQL (1:9) Expected Name, found }" in excinfo.value.message
-
-
-def test_does_not_allow_null_value():
-    # type: () -> None
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse("{ fieldWithNullableStringInput(input: null) }")
-
-    assert 'Syntax Error GraphQL (1:39) Unexpected Name "null"' in excinfo.value.message
-
-
-def test_parses_multi_byte_characters():
-    # type: () -> None
-    result = parse(
-        u"""
-        # This comment has a \u0A0A multi-byte character.
-        { field(arg: "Has a \u0A0A multi-byte character.") }
-    """,
-        no_location=True,
-        no_source=True,
-    )
-    assert result == ast.Document(
-        definitions=[
-            ast.OperationDefinition(
-                operation="query",
-                name=None,
-                variable_definitions=None,
-                directives=[],
-                selection_set=ast.SelectionSet(
-                    selections=[
-                        ast.Field(
-                            alias=None,
-                            name=ast.Name(value=u"field"),
-                            arguments=[
-                                ast.Argument(
-                                    name=ast.Name(value=u"arg"),
-                                    value=ast.StringValue(
-                                        value=u"Has a \u0a0a multi-byte character."
-                                    ),
-                                )
-                            ],
-                            directives=[],
-                            selection_set=None,
-                        )
-                    ]
-                ),
-            )
-        ]
-    )
-
-
-def tesst_allows_non_keywords_anywhere_a_name_is_allowed():
-    non_keywords = [
-        "on",
-        "fragment",
-        "query",
-        "mutation",
-        "subscription",
-        "true",
-        "false",
-    ]
-
-    query_template = """
-    query {keyword} {
-        ... {fragment_name}
-        ... on {keyword} { field }
-    }
-    fragment {fragment_name} on Type {
-        {keyword}({keyword}: ${keyword}) @{keyword}({keyword}: {keyword})
-    }
-    """
-
-    for keyword in non_keywords:
-        fragment_name = keyword
-        if keyword == "on":
-            fragment_name = "a"
-
-        parse(query_template.format(fragment_name=fragment_name, keyword=keyword))
-
-
-def test_parses_kitchen_sink():
-    # type: () -> None
-    parse(KITCHEN_SINK)
-
-
-def test_parses_anonymous_mutation_operations():
-    # type: () -> None
-    parse(
-        """
-        mutation {
-            mutationField
-        }
-    """
-    )
-
-
-def test_parses_anonymous_subscription_operations():
-    # type: () -> None
-    parse(
-        """
-        subscription {
-            mutationField
-        }
-    """
-    )
-
-
-def test_parses_named_mutation_operations():
-    # type: () -> None
-    parse(
-        """
-        mutation Foo {
-            mutationField
-        }
-    """
-    )
-
-
-def test_parses_named_subscription_operations():
-    # type: () -> None
-    parse(
-        """
-        subscription Foo {
-            subscriptionField
-        }
-    """
-    )
-
-
-def test_parse_creates_ast():
-    # type: () -> None
-    source = Source(
-        """{
-  node(id: 4) {
-    id,
-    name
-  }
-}
-"""
-    )
-    result = parse(source)
-
-    assert result == ast.Document(
-        loc=Loc(start=0, end=41, source=source),
-        definitions=[
-            ast.OperationDefinition(
-                loc=Loc(start=0, end=40, source=source),
-                operation="query",
-                name=None,
-                variable_definitions=None,
-                directives=[],
-                selection_set=ast.SelectionSet(
-                    loc=Loc(start=0, end=40, source=source),
-                    selections=[
-                        ast.Field(
-                            loc=Loc(start=4, end=38, source=source),
-                            alias=None,
-                            name=ast.Name(
-                                loc=Loc(start=4, end=8, source=source), value="node"
-                            ),
-                            arguments=[
-                                ast.Argument(
-                                    name=ast.Name(
-                                        loc=Loc(start=9, end=11, source=source),
-                                        value="id",
-                                    ),
-                                    value=ast.IntValue(
-                                        loc=Loc(start=13, end=14, source=source),
-                                        value="4",
-                                    ),
-                                    loc=Loc(start=9, end=14, source=source),
-                                )
-                            ],
-                            directives=[],
-                            selection_set=ast.SelectionSet(
-                                loc=Loc(start=16, end=38, source=source),
-                                selections=[
-                                    ast.Field(
-                                        loc=Loc(start=22, end=24, source=source),
-                                        alias=None,
-                                        name=ast.Name(
-                                            loc=Loc(start=22, end=24, source=source),
-                                            value="id",
-                                        ),
-                                        arguments=[],
-                                        directives=[],
-                                        selection_set=None,
-                                    ),
-                                    ast.Field(
-                                        loc=Loc(start=30, end=34, source=source),
-                                        alias=None,
-                                        name=ast.Name(
-                                            loc=Loc(start=30, end=34, source=source),
-                                            value="name",
-                                        ),
-                                        arguments=[],
-                                        directives=[],
-                                        selection_set=None,
-                                    ),
-                                ],
-                            ),
-                        )
-                    ],
-                ),
-            )
-        ],
-    )
diff --git a/graphql/language/tests/test_printer.py b/graphql/language/tests/test_printer.py
deleted file mode 100644
index 470991d..0000000
--- a/graphql/language/tests/test_printer.py
+++ /dev/null
@@ -1,145 +0,0 @@
-import copy
-
-from pytest import raises
-
-from graphql.language.ast import Field, Name
-from graphql.language.parser import parse
-from graphql.language.printer import print_ast
-
-from .fixtures import KITCHEN_SINK
-
-
-def test_does_not_alter_ast():
-    # type: () -> None
-    ast = parse(KITCHEN_SINK)
-    ast_copy = copy.deepcopy(ast)
-    print_ast(ast)
-    assert ast == ast_copy
-
-
-def test_prints_minimal_ast():
-    # type: () -> None
-    ast = Field(name=Name(loc=None, value="foo"))
-    assert print_ast(ast) == "foo"
-
-
-def test_produces_helpful_error_messages():
-    # type: () -> None
-    bad_ast = {"random": "Data"}
-    with raises(Exception) as excinfo:
-        print_ast(bad_ast)
-    assert "Invalid AST Node" in str(excinfo.value)
-
-
-def test_correctly_prints_query_operation_without_name():
-    # type: () -> None
-    query_ast_shorthanded = parse("query { id, name }")
-    assert (
-        print_ast(query_ast_shorthanded)
-        == """{
-  id
-  name
-}
-"""
-    )
-
-
-def test_correctly_prints_mutation_operation_without_name():
-    # type: () -> None
-    mutation_ast = parse("mutation { id, name }")
-    assert (
-        print_ast(mutation_ast)
-        == """mutation {
-  id
-  name
-}
-"""
-    )
-
-
-def test_correctly_prints_query_with_artifacts():
-    # type: () -> None
-    query_ast_shorthanded = parse("query ($foo: TestType) @testDirective { id, name }")
-    assert (
-        print_ast(query_ast_shorthanded)
-        == """query ($foo: TestType) @testDirective {
-  id
-  name
-}
-"""
-    )
-
-
-def test_correctly_prints_mutation_with_artifacts():
-    # type: () -> None
-    query_ast_shorthanded = parse(
-        "mutation ($foo: TestType) @testDirective { id, name }"
-    )
-    assert (
-        print_ast(query_ast_shorthanded)
-        == """mutation ($foo: TestType) @testDirective {
-  id
-  name
-}
-"""
-    )
-
-
-def test_prints_kitchen_sink():
-    # type: () -> None
-    ast = parse(KITCHEN_SINK)
-    printed = print_ast(ast)
-    assert (
-        printed
-        == """query queryName($foo: ComplexType, $site: Site = MOBILE) {
-  whoever123is: node(id: [123, 456]) {
-    id
-    ... on User @defer {
-      field2 {
-        id
-        alias: field1(first: 10, after: $foo) @include(if: $foo) {
-          id
-          ...frag
-        }
-      }
-    }
-    ... @skip(unless: $foo) {
-      id
-    }
-    ... {
-      id
-    }
-  }
-}
-
-mutation likeStory {
-  like(story: 123) @defer {
-    story {
-      id
-    }
-  }
-}
-
-subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
-  storyLikeSubscribe(input: $input) {
-    story {
-      likers {
-        count
-      }
-      likeSentence {
-        text
-      }
-    }
-  }
-}
-
-fragment frag on Friend {
-  foo(size: $size, bar: $b, obj: {key: "value"})
-}
-
-{
-  unnamed(truthy: true, falsey: false)
-  query
-}
-"""
-    )
diff --git a/graphql/language/tests/test_schema_parser.py b/graphql/language/tests/test_schema_parser.py
deleted file mode 100644
index 6ec58d4..0000000
--- a/graphql/language/tests/test_schema_parser.py
+++ /dev/null
@@ -1,580 +0,0 @@
-from pytest import raises
-
-from graphql import Source, parse
-from graphql.error import GraphQLSyntaxError
-from graphql.language import ast
-from graphql.language.parser import Loc
-from typing import Callable
-
-
-def create_loc_fn(body):
-    # type: (str) -> Callable
-    source = Source(body)
-    return lambda start, end: Loc(start, end, source)
-
-
-def test_parses_simple_type():
-    # type: () -> None
-    body = """
-type Hello {
-  world: String
-}"""
-
-    doc = parse(body)
-    loc = create_loc_fn(body)
-
-    expected = ast.Document(
-        definitions=[
-            ast.ObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(6, 11)),
-                interfaces=[],
-                directives=[],
-                fields=[
-                    ast.FieldDefinition(
-                        name=ast.Name(value="world", loc=loc(16, 21)),
-                        arguments=[],
-                        type=ast.NamedType(
-                            name=ast.Name(value="String", loc=loc(23, 29)),
-                            loc=loc(23, 29),
-                        ),
-                        directives=[],
-                        loc=loc(16, 29),
-                    )
-                ],
-                loc=loc(1, 31),
-            )
-        ],
-        loc=loc(1, 31),
-    )
-    assert doc == expected
-
-
-def test_parses_simple_extension():
-    # type: () -> None
-    body = """
-extend type Hello {
-  world: String
-}"""
-    doc = parse(body)
-    loc = create_loc_fn(body)
-
-    expected = ast.Document(
-        definitions=[
-            ast.TypeExtensionDefinition(
-                definition=ast.ObjectTypeDefinition(
-                    name=ast.Name(value="Hello", loc=loc(13, 18)),
-                    interfaces=[],
-                    directives=[],
-                    fields=[
-                        ast.FieldDefinition(
-                            name=ast.Name(value="world", loc=loc(23, 28)),
-                            arguments=[],
-                            type=ast.NamedType(
-                                name=ast.Name(value="String", loc=loc(30, 36)),
-                                loc=loc(30, 36),
-                            ),
-                            directives=[],
-                            loc=loc(23, 36),
-                        )
-                    ],
-                    loc=loc(8, 38),
-                ),
-                loc=loc(1, 38),
-            )
-        ],
-        loc=loc(1, 38),
-    )
-
-    assert doc == expected
-
-
-def test_simple_non_null_type():
-    # type: () -> None
-    body = """
-type Hello {
-  world: String!
-}"""
-
-    doc = parse(body)
-    loc = create_loc_fn(body)
-    expected = ast.Document(
-        definitions=[
-            ast.ObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(6, 11)),
-                interfaces=[],
-                directives=[],
-                fields=[
-                    ast.FieldDefinition(
-                        name=ast.Name(value="world", loc=loc(16, 21)),
-                        arguments=[],
-                        type=ast.NonNullType(
-                            type=ast.NamedType(
-                                name=ast.Name(value="String", loc=loc(23, 29)),
-                                loc=loc(23, 29),
-                            ),
-                            loc=loc(23, 30),
-                        ),
-                        directives=[],
-                        loc=loc(16, 30),
-                    )
-                ],
-                loc=loc(1, 32),
-            )
-        ],
-        loc=loc(1, 32),
-    )
-    assert doc == expected
-
-
-def test_parses_simple_type_inheriting_interface():
-    # type: () -> None
-    body = "type Hello implements World { }"
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.ObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(5, 10)),
-                interfaces=[
-                    ast.NamedType(
-                        name=ast.Name(value="World", loc=loc(22, 27)), loc=loc(22, 27)
-                    )
-                ],
-                directives=[],
-                fields=[],
-                loc=loc(0, 31),
-            )
-        ],
-        loc=loc(0, 31),
-    )
-
-    assert doc == expected
-
-
-def test_parses_simple_type_inheriting_multiple_interfaces():
-    # type: () -> None
-    body = "type Hello implements Wo, rld { }"
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.ObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(5, 10)),
-                interfaces=[
-                    ast.NamedType(
-                        name=ast.Name(value="Wo", loc=loc(22, 24)), loc=loc(22, 24)
-                    ),
-                    ast.NamedType(
-                        name=ast.Name(value="rld", loc=loc(26, 29)), loc=loc(26, 29)
-                    ),
-                ],
-                directives=[],
-                fields=[],
-                loc=loc(0, 33),
-            )
-        ],
-        loc=loc(0, 33),
-    )
-    assert doc == expected
-
-
-def test_parses_single_value_enum():
-    # type: () -> None
-    body = "enum Hello { WORLD }"
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.EnumTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(5, 10)),
-                directives=[],
-                values=[
-                    ast.EnumValueDefinition(
-                        name=ast.Name(value="WORLD", loc=loc(13, 18)),
-                        directives=[],
-                        loc=loc(13, 18),
-                    )
-                ],
-                loc=loc(0, 20),
-            )
-        ],
-        loc=loc(0, 20),
-    )
-
-    assert doc == expected
-
-
-def test_parses_double_value_enum():
-    # type: () -> None
-    body = "enum Hello { WO, RLD }"
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.EnumTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(5, 10)),
-                directives=[],
-                values=[
-                    ast.EnumValueDefinition(
-                        name=ast.Name(value="WO", loc=loc(13, 15)),
-                        directives=[],
-                        loc=loc(13, 15),
-                    ),
-                    ast.EnumValueDefinition(
-                        name=ast.Name(value="RLD", loc=loc(17, 20)),
-                        directives=[],
-                        loc=loc(17, 20),
-                    ),
-                ],
-                loc=loc(0, 22),
-            )
-        ],
-        loc=loc(0, 22),
-    )
-
-    assert doc == expected
-
-
-def test_parses_simple_interface():
-    # type: () -> None
-    body = """
-interface Hello {
-  world: String
-}
-"""
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.InterfaceTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(11, 16)),
-                directives=[],
-                fields=[
-                    ast.FieldDefinition(
-                        name=ast.Name(value="world", loc=loc(21, 26)),
-                        arguments=[],
-                        type=ast.NamedType(
-                            name=ast.Name(value="String", loc=loc(28, 34)),
-                            loc=loc(28, 34),
-                        ),
-                        directives=[],
-                        loc=loc(21, 34),
-                    )
-                ],
-                loc=loc(1, 36),
-            )
-        ],
-        loc=loc(1, 37),
-    )
-
-    assert doc == expected
-
-
-def test_parses_simple_field_with_arg():
-    # type: () -> None
-    body = """
-type Hello {
-  world(flag: Boolean): String
-}"""
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.ObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(6, 11)),
-                interfaces=[],
-                directives=[],
-                fields=[
-                    ast.FieldDefinition(
-                        name=ast.Name(value="world", loc=loc(16, 21)),
-                        arguments=[
-                            ast.InputValueDefinition(
-                                name=ast.Name(value="flag", loc=loc(22, 26)),
-                                type=ast.NamedType(
-                                    name=ast.Name(value="Boolean", loc=loc(28, 35)),
-                                    loc=loc(28, 35),
-                                ),
-                                default_value=None,
-                                directives=[],
-                                loc=loc(22, 35),
-                            )
-                        ],
-                        type=ast.NamedType(
-                            name=ast.Name(value="String", loc=loc(38, 44)),
-                            loc=loc(38, 44),
-                        ),
-                        directives=[],
-                        loc=loc(16, 44),
-                    )
-                ],
-                loc=loc(1, 46),
-            )
-        ],
-        loc=loc(1, 46),
-    )
-
-    assert doc == expected
-
-
-def test_parses_simple_field_with_arg_with_default_value():
-    # type: () -> None
-    body = """
-type Hello {
-  world(flag: Boolean = true): String
-}"""
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.ObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(6, 11)),
-                interfaces=[],
-                directives=[],
-                fields=[
-                    ast.FieldDefinition(
-                        name=ast.Name(value="world", loc=loc(16, 21)),
-                        arguments=[
-                            ast.InputValueDefinition(
-                                name=ast.Name(value="flag", loc=loc(22, 26)),
-                                type=ast.NamedType(
-                                    name=ast.Name(value="Boolean", loc=loc(28, 35)),
-                                    loc=loc(28, 35),
-                                ),
-                                default_value=ast.BooleanValue(
-                                    value=True, loc=loc(38, 42)
-                                ),
-                                directives=[],
-                                loc=loc(22, 42),
-                            )
-                        ],
-                        type=ast.NamedType(
-                            name=ast.Name(value="String", loc=loc(45, 51)),
-                            loc=loc(45, 51),
-                        ),
-                        directives=[],
-                        loc=loc(16, 51),
-                    )
-                ],
-                loc=loc(1, 53),
-            )
-        ],
-        loc=loc(1, 53),
-    )
-
-    assert doc == expected
-
-
-def test_parses_simple_field_with_list_arg():
-    # type: () -> None
-    body = """
-type Hello {
-  world(things: [String]): String
-}"""
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.ObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(6, 11)),
-                interfaces=[],
-                directives=[],
-                fields=[
-                    ast.FieldDefinition(
-                        name=ast.Name(value="world", loc=loc(16, 21)),
-                        arguments=[
-                            ast.InputValueDefinition(
-                                name=ast.Name(value="things", loc=loc(22, 28)),
-                                type=ast.ListType(
-                                    type=ast.NamedType(
-                                        name=ast.Name(value="String", loc=loc(31, 37)),
-                                        loc=loc(31, 37),
-                                    ),
-                                    loc=loc(30, 38),
-                                ),
-                                default_value=None,
-                                directives=[],
-                                loc=loc(22, 38),
-                            )
-                        ],
-                        type=ast.NamedType(
-                            name=ast.Name(value="String", loc=loc(41, 47)),
-                            loc=loc(41, 47),
-                        ),
-                        directives=[],
-                        loc=loc(16, 47),
-                    )
-                ],
-                loc=loc(1, 49),
-            )
-        ],
-        loc=loc(1, 49),
-    )
-    assert doc == expected
-
-
-def test_parses_simple_field_with_two_args():
-    # type: () -> None
-    body = """
-type Hello {
-  world(argOne: Boolean, argTwo: Int): String
-}"""
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.ObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(6, 11)),
-                interfaces=[],
-                directives=[],
-                fields=[
-                    ast.FieldDefinition(
-                        name=ast.Name(value="world", loc=loc(16, 21)),
-                        arguments=[
-                            ast.InputValueDefinition(
-                                name=ast.Name(value="argOne", loc=loc(22, 28)),
-                                type=ast.NamedType(
-                                    name=ast.Name(value="Boolean", loc=loc(30, 37)),
-                                    loc=loc(30, 37),
-                                ),
-                                default_value=None,
-                                directives=[],
-                                loc=loc(22, 37),
-                            ),
-                            ast.InputValueDefinition(
-                                name=ast.Name(value="argTwo", loc=loc(39, 45)),
-                                type=ast.NamedType(
-                                    name=ast.Name(value="Int", loc=loc(47, 50)),
-                                    loc=loc(47, 50),
-                                ),
-                                default_value=None,
-                                directives=[],
-                                loc=loc(39, 50),
-                            ),
-                        ],
-                        type=ast.NamedType(
-                            name=ast.Name(value="String", loc=loc(53, 59)),
-                            loc=loc(53, 59),
-                        ),
-                        directives=[],
-                        loc=loc(16, 59),
-                    )
-                ],
-                loc=loc(1, 61),
-            )
-        ],
-        loc=loc(1, 61),
-    )
-    assert doc == expected
-
-
-def test_parses_simple_union():
-    # type: () -> None
-    body = "union Hello = World"
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.UnionTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(6, 11)),
-                directives=[],
-                types=[
-                    ast.NamedType(
-                        name=ast.Name(value="World", loc=loc(14, 19)), loc=loc(14, 19)
-                    )
-                ],
-                loc=loc(0, 19),
-            )
-        ],
-        loc=loc(0, 19),
-    )
-    assert doc == expected
-
-
-def test_parses_union_with_two_types():
-    # type: () -> None
-    body = "union Hello = Wo | Rld"
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.UnionTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(6, 11)),
-                directives=[],
-                types=[
-                    ast.NamedType(
-                        name=ast.Name(value="Wo", loc=loc(14, 16)), loc=loc(14, 16)
-                    ),
-                    ast.NamedType(
-                        name=ast.Name(value="Rld", loc=loc(19, 22)), loc=loc(19, 22)
-                    ),
-                ],
-                loc=loc(0, 22),
-            )
-        ],
-        loc=loc(0, 22),
-    )
-    assert doc == expected
-
-
-def test_parses_scalar():
-    # type: () -> None
-    body = "scalar Hello"
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.ScalarTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(7, 12)),
-                directives=[],
-                loc=loc(0, 12),
-            )
-        ],
-        loc=loc(0, 12),
-    )
-    assert doc == expected
-
-
-def test_parses_simple_input_object():
-    # type: () -> None
-    body = """
-input Hello {
-  world: String
-}"""
-    loc = create_loc_fn(body)
-    doc = parse(body)
-    expected = ast.Document(
-        definitions=[
-            ast.InputObjectTypeDefinition(
-                name=ast.Name(value="Hello", loc=loc(7, 12)),
-                directives=[],
-                fields=[
-                    ast.InputValueDefinition(
-                        name=ast.Name(value="world", loc=loc(17, 22)),
-                        type=ast.NamedType(
-                            name=ast.Name(value="String", loc=loc(24, 30)),
-                            loc=loc(24, 30),
-                        ),
-                        default_value=None,
-                        directives=[],
-                        loc=loc(17, 30),
-                    )
-                ],
-                loc=loc(1, 32),
-            )
-        ],
-        loc=loc(1, 32),
-    )
-    assert doc == expected
-
-
-def test_parsing_simple_input_object_with_args_should_fail():
-    # type: () -> None
-    body = """
-input Hello {
-  world(foo: Int): String
-}
-"""
-    with raises(GraphQLSyntaxError) as excinfo:
-        parse(body)
-
-    assert "Syntax Error GraphQL (3:8) Expected :, found (" in excinfo.value.message
diff --git a/graphql/language/tests/test_schema_printer.py b/graphql/language/tests/test_schema_printer.py
deleted file mode 100644
index afa979c..0000000
--- a/graphql/language/tests/test_schema_printer.py
+++ /dev/null
@@ -1,108 +0,0 @@
-from copy import deepcopy
-
-from pytest import raises
-
-from graphql import parse
-from graphql.language import ast
-from graphql.language.printer import print_ast
-
-from .fixtures import SCHEMA_KITCHEN_SINK
-
-
-def test_prints_minimal_ast():
-    # type: () -> None
-    node = ast.ScalarTypeDefinition(name=ast.Name("foo"))
-
-    assert print_ast(node) == "scalar foo"
-
-
-def test_print_produces_helpful_error_messages():
-    # type: () -> None
-    bad_ast = {"random": "Data"}
-    with raises(AssertionError) as excinfo:
-        print_ast(bad_ast)
-
-    assert "Invalid AST Node: {'random': 'Data'}" in str(excinfo.value)
-
-
-def test_does_not_alter_ast():
-    # type: () -> None
-    ast = parse(SCHEMA_KITCHEN_SINK)
-    ast_copy = deepcopy(ast)
-    print_ast(ast)
-    assert ast == ast_copy
-
-
-def test_prints_kitchen_sink():
-    # type: () -> None
-    ast = parse(SCHEMA_KITCHEN_SINK)
-    printed = print_ast(ast)
-
-    expected = """schema {
-  query: QueryType
-  mutation: MutationType
-}
-
-type Foo implements Bar {
-  one: Type
-  two(argument: InputType!): Type
-  three(argument: InputType, other: String): Int
-  four(argument: String = "string"): String
-  five(argument: [String] = ["string", "string"]): String
-  six(argument: InputType = {key: "value"}): Type
-}
-
-type AnnotatedObject @onObject(arg: "value") {
-  annotatedField(arg: Type = "default" @onArg): Type @onField
-}
-
-interface Bar {
-  one: Type
-  four(argument: String = "string"): String
-}
-
-interface AnnotatedInterface @onInterface {
-  annotatedField(arg: Type @onArg): Type @onField
-}
-
-union Feed = Story | Article | Advert
-
-union AnnotatedUnion @onUnion = A | B
-
-scalar CustomScalar
-
-scalar AnnotatedScalar @onScalar
-
-enum Site {
-  DESKTOP
-  MOBILE
-}
-
-enum AnnotatedEnum @onEnum {
-  ANNOTATED_VALUE @onEnumValue
-  OTHER_VALUE
-}
-
-input InputType {
-  key: String!
-  answer: Int = 42
-}
-
-input AnnotatedInput @onInputObjectType {
-  annotatedField: Type @onField
-}
-
-extend type Foo {
-  seven(argument: [String]): Type
-}
-
-extend type Foo @onType {}
-
-type NoFields {}
-
-directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
-
-directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
-"""
-
-    assert printed == expected
diff --git a/graphql/language/tests/test_visitor.py b/graphql/language/tests/test_visitor.py
deleted file mode 100644
index 1392fe3..0000000
--- a/graphql/language/tests/test_visitor.py
+++ /dev/null
@@ -1,1197 +0,0 @@
-from graphql.language.ast import (
-    Document,
-    Field,
-    Name,
-    OperationDefinition,
-    SelectionSet,
-)
-from graphql.language.parser import parse
-from graphql.language.printer import print_ast
-from graphql.language.visitor import (
-    BREAK,
-    REMOVE,
-    ParallelVisitor,
-    TypeInfoVisitor,
-    Visitor,
-    visit,
-)
-from graphql.type import get_named_type, is_composite_type
-from graphql.utils.type_info import TypeInfo
-
-from ...validation.tests.utils import test_schema
-from .fixtures import KITCHEN_SINK
-from graphql.language.ast import Document
-from graphql.language.ast import OperationDefinition
-from graphql.language.ast import SelectionSet
-from typing import Any
-from typing import Optional
-from typing import Union
-from graphql.language.ast import Field
-from graphql.language.ast import Name
-from graphql.language.visitor import _Falsey
-from typing import List
-from graphql.language.ast import Argument
-from graphql.language.ast import IntValue
-
-
-def test_allows_editing_a_node_both_on_enter_and_on_leave():
-    # type: () -> None
-    ast = parse("{ a, b, c { a, b, c } }", no_location=True)
-
-    class TestVisitor(Visitor):
-        def __init__(self):
-            # type: () -> None
-            self.did_enter = False
-            self.did_leave = False
-
-        def enter(
-            self,
-            node,  # type: Union[Document, OperationDefinition, SelectionSet]
-            *args  # type: Any
-        ):
-            # type: (...) -> Optional[OperationDefinition]
-            if isinstance(node, OperationDefinition):
-                self.did_enter = True
-                selection_set = node.selection_set
-                self.selections = None
-                if selection_set:
-                    self.selections = selection_set.selections
-                new_selection_set = SelectionSet(selections=[])
-                return OperationDefinition(
-                    name=node.name,
-                    variable_definitions=node.variable_definitions,
-                    directives=node.directives,
-                    loc=node.loc,
-                    operation=node.operation,
-                    selection_set=new_selection_set,
-                )
-
-        def leave(
-            self,
-            node,  # type: Union[Document, OperationDefinition, SelectionSet]
-            *args  # type: Any
-        ):
-            # type: (...) -> Optional[OperationDefinition]
-            if isinstance(node, OperationDefinition):
-                self.did_leave = True
-                new_selection_set = None
-                if self.selections:
-                    new_selection_set = SelectionSet(selections=self.selections)
-                return OperationDefinition(
-                    name=node.name,
-                    variable_definitions=node.variable_definitions,
-                    directives=node.directives,
-                    loc=node.loc,
-                    operation=node.operation,
-                    selection_set=new_selection_set,
-                )
-
-    visitor = TestVisitor()
-    edited_ast = visit(ast, visitor)
-    assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
-    assert edited_ast == ast
-    assert visitor.did_enter
-    assert visitor.did_leave
-
-
-def test_allows_editing_the_root_node_on_enter_and_on_leave():
-    # type: () -> None
-    ast = parse("{ a, b, c { a, b, c } }", no_location=True)
-
-    definitions = ast.definitions
-
-    class TestVisitor(Visitor):
-        def __init__(self):
-            # type: () -> None
-            self.did_enter = False
-            self.did_leave = False
-
-        def enter(self, node, *args):
-            # type: (Document, *Any) -> Document
-            if isinstance(node, Document):
-                self.did_enter = True
-                return Document(loc=node.loc, definitions=[])
-
-        def leave(self, node, *args):
-            # type: (Document, *Any) -> Document
-            if isinstance(node, Document):
-                self.did_leave = True
-                return Document(loc=node.loc, definitions=definitions)
-
-    visitor = TestVisitor()
-    edited_ast = visit(ast, visitor)
-    assert edited_ast == ast
-    assert visitor.did_enter
-    assert visitor.did_leave
-
-
-def test_allows_for_editing_on_enter():
-    # type: () -> None
-    ast = parse("{ a, b, c { a, b, c } }", no_location=True)
-
-    class TestVisitor(Visitor):
-        def enter(self, node, *args):
-            # type: (Any, *Any) -> Optional[Any]
-            if isinstance(node, Field) and node.name.value == "b":
-                return REMOVE
-
-    edited_ast = visit(ast, TestVisitor())
-
-    assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
-    assert edited_ast == parse("{ a,   c { a,   c } }", no_location=True)
-
-
-def test_allows_for_editing_on_leave():
-    # type: () -> None
-    ast = parse("{ a, b, c { a, b, c } }", no_location=True)
-
-    class TestVisitor(Visitor):
-        def leave(self, node, *args):
-            # type: (Union[Field, Name], *Any) -> Optional[Falsey]
-            if isinstance(node, Field) and node.name.value == "b":
-                return REMOVE
-
-    edited_ast = visit(ast, TestVisitor())
-
-    assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
-    assert edited_ast == parse("{ a,   c { a,   c } }", no_location=True)
-
-
-def test_visits_edited_node():
-    # type: () -> None
-    added_field = Field(name=Name(value="__typename"))
-    ast = parse("{ a { x } }")
-
-    class TestVisitor(Visitor):
-        def __init__(self):
-            # type: () -> None
-            self.did_visit_added_field = False
-
-        def enter(self, node, *args):
-            # type: (Any, *Any) -> Optional[Field]
-            if isinstance(node, Field) and node.name.value == "a":
-                selection_set = node.selection_set
-                selections = []
-                if selection_set:
-                    selections = selection_set.selections
-                new_selection_set = SelectionSet(selections=[added_field] + selections)
-                return Field(name=None, selection_set=new_selection_set)
-            if node is added_field:
-                self.did_visit_added_field = True
-
-    visitor = TestVisitor()
-    visit(ast, visitor)
-    assert visitor.did_visit_added_field
-
-
-def test_allows_skipping_a_subtree():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a, b { x }, c }")
-
-    class TestVisitor(Visitor):
-        def enter(self, node, *args):
-            # type: (Any, *Any) -> Optional[Any]
-            visited.append(["enter", type(node).__name__, getattr(node, "value", None)])
-            if isinstance(node, Field) and node.name.value == "b":
-                return False
-
-        def leave(self, node, *args):
-            # type: (Union[Field, Name, SelectionSet], *Any) -> None
-            visited.append(["leave", type(node).__name__, getattr(node, "value", None)])
-
-    visit(ast, TestVisitor())
-
-    assert visited == [
-        ["enter", "Document", None],
-        ["enter", "OperationDefinition", None],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "a"],
-        ["leave", "Name", "a"],
-        ["leave", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "c"],
-        ["leave", "Name", "c"],
-        ["leave", "Field", None],
-        ["leave", "SelectionSet", None],
-        ["leave", "OperationDefinition", None],
-        ["leave", "Document", None],
-    ]
-
-
-def test_allows_early_exit_while_visiting():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a, b { x }, c }")
-
-    class TestVisitor(Visitor):
-        def enter(self, node, *args):
-            # type: (Any, *Any) -> Optional[Any]
-            visited.append(["enter", type(node).__name__, getattr(node, "value", None)])
-            if isinstance(node, Name) and node.value == "x":
-                return BREAK
-
-        def leave(self, node, *args):
-            # type: (Union[Field, Name], *Any) -> None
-            visited.append(["leave", type(node).__name__, getattr(node, "value", None)])
-
-    visit(ast, TestVisitor())
-
-    assert visited == [
-        ["enter", "Document", None],
-        ["enter", "OperationDefinition", None],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "a"],
-        ["leave", "Name", "a"],
-        ["leave", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "b"],
-        ["leave", "Name", "b"],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "x"],
-    ]
-
-
-def test_allows_a_named_functions_visitor_api():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a, b { x }, c }")
-
-    class TestVisitor(Visitor):
-        def enter_Name(self, node, *args):
-            # type: (Name, *Any) -> None
-            visited.append(["enter", type(node).__name__, getattr(node, "value", None)])
-
-        def enter_SelectionSet(self, node, *args):
-            # type: (SelectionSet, *Any) -> None
-            visited.append(["enter", type(node).__name__, getattr(node, "value", None)])
-
-        def leave_SelectionSet(self, node, *args):
-            # type: (SelectionSet, *Any) -> None
-            visited.append(["leave", type(node).__name__, getattr(node, "value", None)])
-
-    visit(ast, TestVisitor())
-
-    assert visited == [
-        ["enter", "SelectionSet", None],
-        ["enter", "Name", "a"],
-        ["enter", "Name", "b"],
-        ["enter", "SelectionSet", None],
-        ["enter", "Name", "x"],
-        ["leave", "SelectionSet", None],
-        ["enter", "Name", "c"],
-        ["leave", "SelectionSet", None],
-    ]
-
-
-def test_visits_kitchen_sink():
-    # type: () -> None
-    visited = []
-    ast = parse(KITCHEN_SINK)
-
-    class TestVisitor(Visitor):
-        def enter(self, node, key, parent, *args):
-            # type: (Any, Union[None, int, str], Any, *List[Any]) -> None
-            kind = parent and type(parent).__name__
-            if kind == "list":
-                kind = None
-            visited.append(["enter", type(node).__name__, key, kind])
-
-        def leave(self, node, key, parent, *args):
-            # type: (Any, Union[int, str], Any, *List[Any]) -> None
-            kind = parent and type(parent).__name__
-            if kind == "list":
-                kind = None
-            visited.append(["leave", type(node).__name__, key, kind])
-
-    visit(ast, TestVisitor())
-    assert visited == [
-        ["enter", "Document", None, None],
-        ["enter", "OperationDefinition", 0, None],
-        ["enter", "Name", "name", "OperationDefinition"],
-        ["leave", "Name", "name", "OperationDefinition"],
-        ["enter", "VariableDefinition", 0, None],
-        ["enter", "Variable", "variable", "VariableDefinition"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "variable", "VariableDefinition"],
-        ["enter", "NamedType", "type", "VariableDefinition"],
-        ["enter", "Name", "name", "NamedType"],
-        ["leave", "Name", "name", "NamedType"],
-        ["leave", "NamedType", "type", "VariableDefinition"],
-        ["leave", "VariableDefinition", 0, None],
-        ["enter", "VariableDefinition", 1, None],
-        ["enter", "Variable", "variable", "VariableDefinition"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "variable", "VariableDefinition"],
-        ["enter", "NamedType", "type", "VariableDefinition"],
-        ["enter", "Name", "name", "NamedType"],
-        ["leave", "Name", "name", "NamedType"],
-        ["leave", "NamedType", "type", "VariableDefinition"],
-        ["enter", "EnumValue", "default_value", "VariableDefinition"],
-        ["leave", "EnumValue", "default_value", "VariableDefinition"],
-        ["leave", "VariableDefinition", 1, None],
-        ["enter", "SelectionSet", "selection_set", "OperationDefinition"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "alias", "Field"],
-        ["leave", "Name", "alias", "Field"],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "Argument", 0, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "ListValue", "value", "Argument"],
-        ["enter", "IntValue", 0, None],
-        ["leave", "IntValue", 0, None],
-        ["enter", "IntValue", 1, None],
-        ["leave", "IntValue", 1, None],
-        ["leave", "ListValue", "value", "Argument"],
-        ["leave", "Argument", 0, None],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 0, None],
-        ["enter", "InlineFragment", 1, None],
-        ["enter", "NamedType", "type_condition", "InlineFragment"],
-        ["enter", "Name", "name", "NamedType"],
-        ["leave", "Name", "name", "NamedType"],
-        ["leave", "NamedType", "type_condition", "InlineFragment"],
-        ["enter", "Directive", 0, None],
-        ["enter", "Name", "name", "Directive"],
-        ["leave", "Name", "name", "Directive"],
-        ["leave", "Directive", 0, None],
-        ["enter", "SelectionSet", "selection_set", "InlineFragment"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 0, None],
-        ["enter", "Field", 1, None],
-        ["enter", "Name", "alias", "Field"],
-        ["leave", "Name", "alias", "Field"],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "Argument", 0, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "IntValue", "value", "Argument"],
-        ["leave", "IntValue", "value", "Argument"],
-        ["leave", "Argument", 0, None],
-        ["enter", "Argument", 1, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "Variable", "value", "Argument"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "value", "Argument"],
-        ["leave", "Argument", 1, None],
-        ["enter", "Directive", 0, None],
-        ["enter", "Name", "name", "Directive"],
-        ["leave", "Name", "name", "Directive"],
-        ["enter", "Argument", 0, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "Variable", "value", "Argument"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "value", "Argument"],
-        ["leave", "Argument", 0, None],
-        ["leave", "Directive", 0, None],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 0, None],
-        ["enter", "FragmentSpread", 1, None],
-        ["enter", "Name", "name", "FragmentSpread"],
-        ["leave", "Name", "name", "FragmentSpread"],
-        ["leave", "FragmentSpread", 1, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 1, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "InlineFragment"],
-        ["leave", "InlineFragment", 1, None],
-        ["enter", "InlineFragment", 2, None],
-        ["enter", "Directive", 0, None],
-        ["enter", "Name", "name", "Directive"],
-        ["leave", "Name", "name", "Directive"],
-        ["enter", "Argument", 0, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "Variable", "value", "Argument"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "value", "Argument"],
-        ["leave", "Argument", 0, None],
-        ["leave", "Directive", 0, None],
-        ["enter", "SelectionSet", "selection_set", "InlineFragment"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "InlineFragment"],
-        ["leave", "InlineFragment", 2, None],
-        ["enter", "InlineFragment", 3, None],
-        ["enter", "SelectionSet", "selection_set", "InlineFragment"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "InlineFragment"],
-        ["leave", "InlineFragment", 3, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "OperationDefinition"],
-        ["leave", "OperationDefinition", 0, None],
-        ["enter", "OperationDefinition", 1, None],
-        ["enter", "Name", "name", "OperationDefinition"],
-        ["leave", "Name", "name", "OperationDefinition"],
-        ["enter", "SelectionSet", "selection_set", "OperationDefinition"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "Argument", 0, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "IntValue", "value", "Argument"],
-        ["leave", "IntValue", "value", "Argument"],
-        ["leave", "Argument", 0, None],
-        ["enter", "Directive", 0, None],
-        ["enter", "Name", "name", "Directive"],
-        ["leave", "Name", "name", "Directive"],
-        ["leave", "Directive", 0, None],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "OperationDefinition"],
-        ["leave", "OperationDefinition", 1, None],
-        ["enter", "OperationDefinition", 2, None],
-        ["enter", "Name", "name", "OperationDefinition"],
-        ["leave", "Name", "name", "OperationDefinition"],
-        ["enter", "VariableDefinition", 0, None],
-        ["enter", "Variable", "variable", "VariableDefinition"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "variable", "VariableDefinition"],
-        ["enter", "NamedType", "type", "VariableDefinition"],
-        ["enter", "Name", "name", "NamedType"],
-        ["leave", "Name", "name", "NamedType"],
-        ["leave", "NamedType", "type", "VariableDefinition"],
-        ["leave", "VariableDefinition", 0, None],
-        ["enter", "SelectionSet", "selection_set", "OperationDefinition"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "Argument", 0, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "Variable", "value", "Argument"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "value", "Argument"],
-        ["leave", "Argument", 0, None],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 0, None],
-        ["enter", "Field", 1, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "SelectionSet", "selection_set", "Field"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 1, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "Field"],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "OperationDefinition"],
-        ["leave", "OperationDefinition", 2, None],
-        ["enter", "FragmentDefinition", 3, None],
-        ["enter", "Name", "name", "FragmentDefinition"],
-        ["leave", "Name", "name", "FragmentDefinition"],
-        ["enter", "NamedType", "type_condition", "FragmentDefinition"],
-        ["enter", "Name", "name", "NamedType"],
-        ["leave", "Name", "name", "NamedType"],
-        ["leave", "NamedType", "type_condition", "FragmentDefinition"],
-        ["enter", "SelectionSet", "selection_set", "FragmentDefinition"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "Argument", 0, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "Variable", "value", "Argument"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "value", "Argument"],
-        ["leave", "Argument", 0, None],
-        ["enter", "Argument", 1, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "Variable", "value", "Argument"],
-        ["enter", "Name", "name", "Variable"],
-        ["leave", "Name", "name", "Variable"],
-        ["leave", "Variable", "value", "Argument"],
-        ["leave", "Argument", 1, None],
-        ["enter", "Argument", 2, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "ObjectValue", "value", "Argument"],
-        ["enter", "ObjectField", 0, None],
-        ["enter", "Name", "name", "ObjectField"],
-        ["leave", "Name", "name", "ObjectField"],
-        ["enter", "StringValue", "value", "ObjectField"],
-        ["leave", "StringValue", "value", "ObjectField"],
-        ["leave", "ObjectField", 0, None],
-        ["leave", "ObjectValue", "value", "Argument"],
-        ["leave", "Argument", 2, None],
-        ["leave", "Field", 0, None],
-        ["leave", "SelectionSet", "selection_set", "FragmentDefinition"],
-        ["leave", "FragmentDefinition", 3, None],
-        ["enter", "OperationDefinition", 4, None],
-        ["enter", "SelectionSet", "selection_set", "OperationDefinition"],
-        ["enter", "Field", 0, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["enter", "Argument", 0, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "BooleanValue", "value", "Argument"],
-        ["leave", "BooleanValue", "value", "Argument"],
-        ["leave", "Argument", 0, None],
-        ["enter", "Argument", 1, None],
-        ["enter", "Name", "name", "Argument"],
-        ["leave", "Name", "name", "Argument"],
-        ["enter", "BooleanValue", "value", "Argument"],
-        ["leave", "BooleanValue", "value", "Argument"],
-        ["leave", "Argument", 1, None],
-        ["leave", "Field", 0, None],
-        ["enter", "Field", 1, None],
-        ["enter", "Name", "name", "Field"],
-        ["leave", "Name", "name", "Field"],
-        ["leave", "Field", 1, None],
-        ["leave", "SelectionSet", "selection_set", "OperationDefinition"],
-        ["leave", "OperationDefinition", 4, None],
-        ["leave", "Document", None, None],
-    ]
-
-
-def test_visits_in_pararell_allows_skipping_a_subtree():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a, b { x }, c }")
-
-    class TestVisitor(Visitor):
-        def enter(self, node, key, parent, *args):
-            # type: (Any, Union[None, int, str], Any, *List[Any]) -> Optional[Any]
-            visited.append(["enter", type(node).__name__, getattr(node, "value", None)])
-            if type(node).__name__ == "Field" and node.name.value == "b":
-                return False
-
-        def leave(
-            self,
-            node,  # type: Union[Field, Name, SelectionSet]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Field], Field, OperationDefinition]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> None
-            visited.append(["leave", type(node).__name__, getattr(node, "value", None)])
-
-    visit(ast, ParallelVisitor([TestVisitor()]))
-    assert visited == [
-        ["enter", "Document", None],
-        ["enter", "OperationDefinition", None],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "a"],
-        ["leave", "Name", "a"],
-        ["leave", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "c"],
-        ["leave", "Name", "c"],
-        ["leave", "Field", None],
-        ["leave", "SelectionSet", None],
-        ["leave", "OperationDefinition", None],
-        ["leave", "Document", None],
-    ]
-
-
-def test_visits_in_pararell_allows_skipping_different_subtrees():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a { x }, b { y} }")
-
-    class TestVisitor(Visitor):
-        def __init__(self, name):
-            # type: (str) -> None
-            self.name = name
-
-        def enter(
-            self,
-            node,  # type: Union[Document, OperationDefinition, SelectionSet]
-            key,  # type: Union[None, int, str]
-            parent,  # type: Union[List[OperationDefinition], None, OperationDefinition]
-            *args  # type: Any
-        ):
-            # type: (...) -> Optional[Any]
-            visited.append(
-                [
-                    "no-{}".format(self.name),
-                    "enter",
-                    type(node).__name__,
-                    getattr(node, "value", None),
-                ]
-            )
-            if type(node).__name__ == "Field" and node.name.value == self.name:
-                return False
-
-        def leave(
-            self,
-            node,  # type: Union[Field, Name, SelectionSet]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Field], Field]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> None
-            visited.append(
-                [
-                    "no-{}".format(self.name),
-                    "leave",
-                    type(node).__name__,
-                    getattr(node, "value", None),
-                ]
-            )
-
-    visit(ast, ParallelVisitor([TestVisitor("a"), TestVisitor("b")]))
-    assert visited == [
-        ["no-a", "enter", "Document", None],
-        ["no-b", "enter", "Document", None],
-        ["no-a", "enter", "OperationDefinition", None],
-        ["no-b", "enter", "OperationDefinition", None],
-        ["no-a", "enter", "SelectionSet", None],
-        ["no-b", "enter", "SelectionSet", None],
-        ["no-a", "enter", "Field", None],
-        ["no-b", "enter", "Field", None],
-        ["no-b", "enter", "Name", "a"],
-        ["no-b", "leave", "Name", "a"],
-        ["no-b", "enter", "SelectionSet", None],
-        ["no-b", "enter", "Field", None],
-        ["no-b", "enter", "Name", "x"],
-        ["no-b", "leave", "Name", "x"],
-        ["no-b", "leave", "Field", None],
-        ["no-b", "leave", "SelectionSet", None],
-        ["no-b", "leave", "Field", None],
-        ["no-a", "enter", "Field", None],
-        ["no-b", "enter", "Field", None],
-        ["no-a", "enter", "Name", "b"],
-        ["no-a", "leave", "Name", "b"],
-        ["no-a", "enter", "SelectionSet", None],
-        ["no-a", "enter", "Field", None],
-        ["no-a", "enter", "Name", "y"],
-        ["no-a", "leave", "Name", "y"],
-        ["no-a", "leave", "Field", None],
-        ["no-a", "leave", "SelectionSet", None],
-        ["no-a", "leave", "Field", None],
-        ["no-a", "leave", "SelectionSet", None],
-        ["no-b", "leave", "SelectionSet", None],
-        ["no-a", "leave", "OperationDefinition", None],
-        ["no-b", "leave", "OperationDefinition", None],
-        ["no-a", "leave", "Document", None],
-        ["no-b", "leave", "Document", None],
-    ]
-
-
-def test_visits_in_pararell_allows_early_exit_while_visiting():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a, b { x }, c }")
-
-    class TestVisitor(Visitor):
-        def enter(self, node, key, parent, *args):
-            # type: (Any, Union[None, int, str], Any, *List[Any]) -> None
-            visited.append(["enter", type(node).__name__, getattr(node, "value", None)])
-
-        def leave(
-            self,
-            node,  # type: Union[Field, Name]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Field], Field]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> Optional[object]
-            visited.append(["leave", type(node).__name__, getattr(node, "value", None)])
-            if type(node).__name__ == "Name" and node.value == "x":
-                return BREAK
-
-    visit(ast, ParallelVisitor([TestVisitor()]))
-    assert visited == [
-        ["enter", "Document", None],
-        ["enter", "OperationDefinition", None],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "a"],
-        ["leave", "Name", "a"],
-        ["leave", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "b"],
-        ["leave", "Name", "b"],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "x"],
-        ["leave", "Name", "x"],
-    ]
-
-
-def test_visits_in_pararell_allows_early_exit_from_different_points():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a { y }, b { x } }")
-
-    class TestVisitor(Visitor):
-        def __init__(self, name):
-            # type: (str) -> None
-            self.name = name
-
-        def enter(
-            self,
-            node,  # type: Union[Document, OperationDefinition, SelectionSet]
-            key,  # type: Union[None, int, str]
-            parent,  # type: Union[List[OperationDefinition], None, OperationDefinition]
-            *args  # type: Any
-        ):
-            # type: (...) -> None
-            visited.append(
-                [
-                    "break-{}".format(self.name),
-                    "enter",
-                    type(node).__name__,
-                    getattr(node, "value", None),
-                ]
-            )
-
-        def leave(
-            self,
-            node,  # type: Union[Field, Name]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Field], Field]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> Optional[Any]
-            visited.append(
-                [
-                    "break-{}".format(self.name),
-                    "leave",
-                    type(node).__name__,
-                    getattr(node, "value", None),
-                ]
-            )
-            if type(node).__name__ == "Field" and node.name.value == self.name:
-                return BREAK
-
-    visit(ast, ParallelVisitor([TestVisitor("a"), TestVisitor("b")]))
-    assert visited == [
-        ["break-a", "enter", "Document", None],
-        ["break-b", "enter", "Document", None],
-        ["break-a", "enter", "OperationDefinition", None],
-        ["break-b", "enter", "OperationDefinition", None],
-        ["break-a", "enter", "SelectionSet", None],
-        ["break-b", "enter", "SelectionSet", None],
-        ["break-a", "enter", "Field", None],
-        ["break-b", "enter", "Field", None],
-        ["break-a", "enter", "Name", "a"],
-        ["break-b", "enter", "Name", "a"],
-        ["break-a", "leave", "Name", "a"],
-        ["break-b", "leave", "Name", "a"],
-        ["break-a", "enter", "SelectionSet", None],
-        ["break-b", "enter", "SelectionSet", None],
-        ["break-a", "enter", "Field", None],
-        ["break-b", "enter", "Field", None],
-        ["break-a", "enter", "Name", "y"],
-        ["break-b", "enter", "Name", "y"],
-        ["break-a", "leave", "Name", "y"],
-        ["break-b", "leave", "Name", "y"],
-        ["break-a", "leave", "Field", None],
-        ["break-b", "leave", "Field", None],
-        ["break-a", "leave", "SelectionSet", None],
-        ["break-b", "leave", "SelectionSet", None],
-        ["break-a", "leave", "Field", None],
-        ["break-b", "leave", "Field", None],
-        ["break-b", "enter", "Field", None],
-        ["break-b", "enter", "Name", "b"],
-        ["break-b", "leave", "Name", "b"],
-        ["break-b", "enter", "SelectionSet", None],
-        ["break-b", "enter", "Field", None],
-        ["break-b", "enter", "Name", "x"],
-        ["break-b", "leave", "Name", "x"],
-        ["break-b", "leave", "Field", None],
-        ["break-b", "leave", "SelectionSet", None],
-        ["break-b", "leave", "Field", None],
-    ]
-
-
-def test_visits_in_pararell_allows_for_editing_on_enter():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a, b, c { a, b, c } }", no_location=True)
-
-    class TestVisitor1(Visitor):
-        def enter(self, node, key, parent, *args):
-            # type: (Any, Union[None, int, str], Any, *List[Any]) -> Optional[Any]
-            if type(node).__name__ == "Field" and node.name.value == "b":
-                return REMOVE
-
-    class TestVisitor2(Visitor):
-        def enter(self, node, key, parent, *args):
-            # type: (Any, Union[None, int, str], Any, *List[Any]) -> None
-            visited.append(["enter", type(node).__name__, getattr(node, "value", None)])
-
-        def leave(
-            self,
-            node,  # type: Union[Field, Name]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Field], Field]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> None
-            visited.append(["leave", type(node).__name__, getattr(node, "value", None)])
-
-    edited_ast = visit(ast, ParallelVisitor([TestVisitor1(), TestVisitor2()]))
-
-    assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
-    assert edited_ast == parse("{ a,    c { a,    c } }", no_location=True)
-
-    assert visited == [
-        ["enter", "Document", None],
-        ["enter", "OperationDefinition", None],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "a"],
-        ["leave", "Name", "a"],
-        ["leave", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "c"],
-        ["leave", "Name", "c"],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "a"],
-        ["leave", "Name", "a"],
-        ["leave", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "c"],
-        ["leave", "Name", "c"],
-        ["leave", "Field", None],
-        ["leave", "SelectionSet", None],
-        ["leave", "Field", None],
-        ["leave", "SelectionSet", None],
-        ["leave", "OperationDefinition", None],
-        ["leave", "Document", None],
-    ]
-
-
-def test_visits_in_pararell_allows_for_editing_on_leave():
-    # type: () -> None
-    visited = []
-    ast = parse("{ a, b, c { a, b, c } }", no_location=True)
-
-    class TestVisitor1(Visitor):
-        def leave(
-            self,
-            node,  # type: Union[Field, Name]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Field], Field]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> Optional[Falsey]
-            if type(node).__name__ == "Field" and node.name.value == "b":
-                return REMOVE
-
-    class TestVisitor2(Visitor):
-        def enter(self, node, key, parent, *args):
-            # type: (Any, Union[None, int, str], Any, *List[Any]) -> None
-            visited.append(["enter", type(node).__name__, getattr(node, "value", None)])
-
-        def leave(
-            self,
-            node,  # type: Union[Field, Name]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Field], Field]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> None
-            visited.append(["leave", type(node).__name__, getattr(node, "value", None)])
-
-    edited_ast = visit(ast, ParallelVisitor([TestVisitor1(), TestVisitor2()]))
-
-    assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
-    assert edited_ast == parse("{ a,    c { a,    c } }", no_location=True)
-
-    assert visited == [
-        ["enter", "Document", None],
-        ["enter", "OperationDefinition", None],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "a"],
-        ["leave", "Name", "a"],
-        ["leave", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "b"],
-        ["leave", "Name", "b"],
-        ["enter", "Field", None],
-        ["enter", "Name", "c"],
-        ["leave", "Name", "c"],
-        ["enter", "SelectionSet", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "a"],
-        ["leave", "Name", "a"],
-        ["leave", "Field", None],
-        ["enter", "Field", None],
-        ["enter", "Name", "b"],
-        ["leave", "Name", "b"],
-        ["enter", "Field", None],
-        ["enter", "Name", "c"],
-        ["leave", "Name", "c"],
-        ["leave", "Field", None],
-        ["leave", "SelectionSet", None],
-        ["leave", "Field", None],
-        ["leave", "SelectionSet", None],
-        ["leave", "OperationDefinition", None],
-        ["leave", "Document", None],
-    ]
-
-
-def test_visits_with_typeinfo_maintains_type_info_during_visit():
-    # type: () -> None
-    visited = []
-    ast = parse("{ human(id: 4) { name, pets { name }, unknown } }")
-
-    type_info = TypeInfo(test_schema)
-
-    class TestVisitor(Visitor):
-        def enter(self, node, key, parent, *args):
-            # type: (Any, Union[None, int, str], Any, *List[Any]) -> None
-            parent_type = type_info.get_parent_type()
-            _type = type_info.get_type()
-            input_type = type_info.get_input_type()
-            visited.append(
-                [
-                    "enter",
-                    type(node).__name__,
-                    node.value if type(node).__name__ == "Name" else None,
-                    str(parent_type) if parent_type else None,
-                    str(_type) if _type else None,
-                    str(input_type) if input_type else None,
-                ]
-            )
-
-        def leave(
-            self,
-            node,  # type: Union[Argument, IntValue, Name]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Argument], Argument, Field]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> None
-            parent_type = type_info.get_parent_type()
-            _type = type_info.get_type()
-            input_type = type_info.get_input_type()
-            visited.append(
-                [
-                    "leave",
-                    type(node).__name__,
-                    node.value if type(node).__name__ == "Name" else None,
-                    str(parent_type) if parent_type else None,
-                    str(_type) if _type else None,
-                    str(input_type) if input_type else None,
-                ]
-            )
-
-    visit(ast, TypeInfoVisitor(type_info, TestVisitor()))
-    assert visited == [
-        ["enter", "Document", None, None, None, None],
-        ["enter", "OperationDefinition", None, None, "QueryRoot", None],
-        ["enter", "SelectionSet", None, "QueryRoot", "QueryRoot", None],
-        ["enter", "Field", None, "QueryRoot", "Human", None],
-        ["enter", "Name", "human", "QueryRoot", "Human", None],
-        ["leave", "Name", "human", "QueryRoot", "Human", None],
-        ["enter", "Argument", None, "QueryRoot", "Human", "ID"],
-        ["enter", "Name", "id", "QueryRoot", "Human", "ID"],
-        ["leave", "Name", "id", "QueryRoot", "Human", "ID"],
-        ["enter", "IntValue", None, "QueryRoot", "Human", "ID"],
-        ["leave", "IntValue", None, "QueryRoot", "Human", "ID"],
-        ["leave", "Argument", None, "QueryRoot", "Human", "ID"],
-        ["enter", "SelectionSet", None, "Human", "Human", None],
-        ["enter", "Field", None, "Human", "String", None],
-        ["enter", "Name", "name", "Human", "String", None],
-        ["leave", "Name", "name", "Human", "String", None],
-        ["leave", "Field", None, "Human", "String", None],
-        ["enter", "Field", None, "Human", "[Pet]", None],
-        ["enter", "Name", "pets", "Human", "[Pet]", None],
-        ["leave", "Name", "pets", "Human", "[Pet]", None],
-        ["enter", "SelectionSet", None, "Pet", "[Pet]", None],
-        ["enter", "Field", None, "Pet", "String", None],
-        ["enter", "Name", "name", "Pet", "String", None],
-        ["leave", "Name", "name", "Pet", "String", None],
-        ["leave", "Field", None, "Pet", "String", None],
-        ["leave", "SelectionSet", None, "Pet", "[Pet]", None],
-        ["leave", "Field", None, "Human", "[Pet]", None],
-        ["enter", "Field", None, "Human", None, None],
-        ["enter", "Name", "unknown", "Human", None, None],
-        ["leave", "Name", "unknown", "Human", None, None],
-        ["leave", "Field", None, "Human", None, None],
-        ["leave", "SelectionSet", None, "Human", "Human", None],
-        ["leave", "Field", None, "QueryRoot", "Human", None],
-        ["leave", "SelectionSet", None, "QueryRoot", "QueryRoot", None],
-        ["leave", "OperationDefinition", None, None, "QueryRoot", None],
-        ["leave", "Document", None, None, None, None],
-    ]
-
-
-def test_visits_with_typeinfo_maintains_type_info_during_edit():
-    # type: () -> None
-    visited = []
-    ast = parse("{ human(id: 4) { name, pets }, alien }")
-
-    type_info = TypeInfo(test_schema)
-
-    class TestVisitor(Visitor):
-        def enter(self, node, key, parent, *args):
-            # type: (Any, Union[None, int, str], Any, *List[Any]) -> Optional[Any]
-            parent_type = type_info.get_parent_type()
-            _type = type_info.get_type()
-            input_type = type_info.get_input_type()
-            visited.append(
-                [
-                    "enter",
-                    type(node).__name__,
-                    node.value if type(node).__name__ == "Name" else None,
-                    str(parent_type) if parent_type else None,
-                    str(_type) if _type else None,
-                    str(input_type) if input_type else None,
-                ]
-            )
-
-            # Make a query valid by adding missing selection sets.
-            if (
-                type(node).__name__ == "Field"
-                and not node.selection_set
-                and is_composite_type(get_named_type(_type))
-            ):
-                return Field(
-                    alias=node.alias,
-                    name=node.name,
-                    arguments=node.arguments,
-                    directives=node.directives,
-                    selection_set=SelectionSet([Field(name=Name(value="__typename"))]),
-                )
-
-        def leave(
-            self,
-            node,  # type: Union[Argument, IntValue, Name]
-            key,  # type: Union[int, str]
-            parent,  # type: Union[List[Argument], Argument, Field]
-            *args  # type: List[Any]
-        ):
-            # type: (...) -> None
-            parent_type = type_info.get_parent_type()
-            _type = type_info.get_type()
-            input_type = type_info.get_input_type()
-            visited.append(
-                [
-                    "leave",
-                    type(node).__name__,
-                    node.value if type(node).__name__ == "Name" else None,
-                    str(parent_type) if parent_type else None,
-                    str(_type) if _type else None,
-                    str(input_type) if input_type else None,
-                ]
-            )
-
-    edited_ast = visit(ast, TypeInfoVisitor(type_info, TestVisitor()))
-
-    # assert print_ast(ast) == print_ast(parse(
-    #     '{ human(id: 4) { name, pets }, alien }'
-    # ))
-    assert print_ast(edited_ast) == print_ast(
-        parse("{ human(id: 4) { name, pets { __typename } }, alien { __typename } }")
-    )
-    assert visited == [
-        ["enter", "Document", None, None, None, None],
-        ["enter", "OperationDefinition", None, None, "QueryRoot", None],
-        ["enter", "SelectionSet", None, "QueryRoot", "QueryRoot", None],
-        ["enter", "Field", None, "QueryRoot", "Human", None],
-        ["enter", "Name", "human", "QueryRoot", "Human", None],
-        ["leave", "Name", "human", "QueryRoot", "Human", None],
-        ["enter", "Argument", None, "QueryRoot", "Human", "ID"],
-        ["enter", "Name", "id", "QueryRoot", "Human", "ID"],
-        ["leave", "Name", "id", "QueryRoot", "Human", "ID"],
-        ["enter", "IntValue", None, "QueryRoot", "Human", "ID"],
-        ["leave", "IntValue", None, "QueryRoot", "Human", "ID"],
-        ["leave", "Argument", None, "QueryRoot", "Human", "ID"],
-        ["enter", "SelectionSet", None, "Human", "Human", None],
-        ["enter", "Field", None, "Human", "String", None],
-        ["enter", "Name", "name", "Human", "String", None],
-        ["leave", "Name", "name", "Human", "String", None],
-        ["leave", "Field", None, "Human", "String", None],
-        ["enter", "Field", None, "Human", "[Pet]", None],
-        ["enter", "Name", "pets", "Human", "[Pet]", None],
-        ["leave", "Name", "pets", "Human", "[Pet]", None],
-        ["enter", "SelectionSet", None, "Pet", "[Pet]", None],
-        ["enter", "Field", None, "Pet", "String!", None],
-        ["enter", "Name", "__typename", "Pet", "String!", None],
-        ["leave", "Name", "__typename", "Pet", "String!", None],
-        ["leave", "Field", None, "Pet", "String!", None],
-        ["leave", "SelectionSet", None, "Pet", "[Pet]", None],
-        ["leave", "Field", None, "Human", "[Pet]", None],
-        ["leave", "SelectionSet", None, "Human", "Human", None],
-        ["leave", "Field", None, "QueryRoot", "Human", None],
-        ["enter", "Field", None, "QueryRoot", "Alien", None],
-        ["enter", "Name", "alien", "QueryRoot", "Alien", None],
-        ["leave", "Name", "alien", "QueryRoot", "Alien", None],
-        ["enter", "SelectionSet", None, "Alien", "Alien", None],
-        ["enter", "Field", None, "Alien", "String!", None],
-        ["enter", "Name", "__typename", "Alien", "String!", None],
-        ["leave", "Name", "__typename", "Alien", "String!", None],
-        ["leave", "Field", None, "Alien", "String!", None],
-        ["leave", "SelectionSet", None, "Alien", "Alien", None],
-        ["leave", "Field", None, "QueryRoot", "Alien", None],
-        ["leave", "SelectionSet", None, "QueryRoot", "QueryRoot", None],
-        ["leave", "OperationDefinition", None, None, "QueryRoot", None],
-        ["leave", "Document", None, None, None, None],
-    ]
diff --git a/graphql/language/tests/test_visitor_meta.py b/graphql/language/tests/test_visitor_meta.py
deleted file mode 100644
index d40bb47..0000000
--- a/graphql/language/tests/test_visitor_meta.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from graphql.language import ast
-from graphql.language.visitor import Visitor
-
-
-def test_visitor_meta_creates_enter_and_leave_handlers():
-    # type: () -> None
-    class MyVisitor(Visitor):
-        def enter_OperationDefinition(self):
-            pass
-
-        def leave_OperationDefinition(self):
-            pass
-
-    assert MyVisitor._get_enter_handler(ast.OperationDefinition)
-    assert MyVisitor._get_leave_handler(ast.OperationDefinition)
-
-
-def test_visitor_inherits_parent_definitions():
-    # type: () -> None
-    class MyVisitor(Visitor):
-        def enter_OperationDefinition(self):
-            pass
-
-        def leave_OperationDefinition(self):
-            pass
-
-    assert MyVisitor._get_enter_handler(ast.OperationDefinition)
-    assert MyVisitor._get_leave_handler(ast.OperationDefinition)
-
-    class MyVisitorSubclassed(MyVisitor):
-        def enter_FragmentDefinition(self):
-            pass
-
-        def leave_FragmentDefinition(self):
-            pass
-
-    assert MyVisitorSubclassed._get_enter_handler(ast.OperationDefinition)
-    assert MyVisitorSubclassed._get_leave_handler(ast.OperationDefinition)
-    assert MyVisitorSubclassed._get_enter_handler(ast.FragmentDefinition)
-    assert MyVisitorSubclassed._get_leave_handler(ast.FragmentDefinition)
diff --git a/graphql/language/visitor.py b/graphql/language/visitor.py
deleted file mode 100644
index 42b1a2c..0000000
--- a/graphql/language/visitor.py
+++ /dev/null
@@ -1,294 +0,0 @@
-from copy import copy
-
-import six
-
-from . import ast
-from .visitor_meta import QUERY_DOCUMENT_KEYS, VisitorMeta
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, List, Optional, Union, Tuple, Dict
-    from ..utils.type_info import TypeInfo
-
-
-class _Falsey(object):
-    def __nonzero__(self):
-        return False
-
-    def __bool__(self):
-        # type: () -> bool
-        return False
-
-
-class _Break(object):
-    pass
-
-
-BREAK = _Break()
-REMOVE = _Falsey()
-
-
-class Stack(object):
-    __slots__ = "in_array", "index", "keys", "edits", "prev"
-
-    def __init__(self, in_array, index, keys, edits, prev):
-        # type: (bool, int, Any, List[Tuple[Union[str, int], Any]], Optional[Stack]) -> None
-        self.in_array = in_array
-        self.index = index
-        self.keys = keys
-        self.edits = edits
-        self.prev = prev
-
-
-def visit(root, visitor, key_map=None):
-    # type: (Union[ast.Node, List[ast.Node]], Visitor, Optional[Dict[ast.Node, Tuple]]) -> Any
-    visitor_keys = key_map or QUERY_DOCUMENT_KEYS
-
-    stack = None  # type: Optional[Stack]
-    in_array = isinstance(root, list)
-    keys = [root]
-    index = -1
-    edits = []  # type: List[Tuple[Union[str, int], Any]]
-    parent = None  # type: Optional[ast.Node]
-    path = []  # type: List
-    ancestors = []  # type: List[ast.Node]
-    new_root = root
-    leave = visitor.leave
-    enter = visitor.enter
-    path_pop = path.pop
-    ancestors_pop = ancestors.pop
-    path_append = path.append
-    ancestors_append = ancestors.append
-
-    while True:
-        index += 1
-        is_leaving = index == len(keys)
-        is_edited = is_leaving and edits
-        if is_leaving:
-            key = path_pop() if ancestors else None
-            node = parent
-            parent = ancestors_pop() if ancestors else None
-
-            if is_edited:
-                if in_array:
-                    node = list(node)  # type: ignore
-                else:
-                    node = copy(node)
-                edit_offset = 0
-                for edit_key, edit_value in edits:
-                    if in_array:
-                        edit_key -= edit_offset  # type: ignore
-
-                    if in_array and edit_value is REMOVE:
-                        node.pop(edit_key)  # type: ignore
-                        edit_offset += 1
-
-                    else:
-                        if isinstance(node, list):
-                            node[edit_key] = edit_value
-                        else:
-                            setattr(node, edit_key, edit_value)  # type: ignore
-
-            index = stack.index  # type: ignore
-            keys = stack.keys  # type: ignore
-            edits = stack.edits  # type: ignore
-            in_array = stack.in_array  # type: ignore
-            stack = stack.prev  # type: ignore
-
-        else:
-            if parent:
-                key = index if in_array else keys[index]
-                if isinstance(parent, list):
-                    node = parent[key]
-                else:
-                    node = getattr(parent, key, None)
-
-            else:
-                key = None
-                node = new_root  # type: ignore
-
-            if node is REMOVE or node is None:
-                continue
-
-            if parent:
-                path_append(key)
-
-        result = None
-
-        if not isinstance(node, list):
-            assert isinstance(node, ast.Node), "Invalid AST Node: " + repr(node)
-
-            if is_leaving:
-                result = leave(node, key, parent, path, ancestors)
-
-            else:
-                result = enter(node, key, parent, path, ancestors)
-
-            if result is BREAK:
-                break
-
-            if result is False:
-                if not is_leaving:
-                    path_pop()
-                    continue
-
-            elif result is not None:
-                edits.append((key, result))
-                if not is_leaving:
-                    if isinstance(result, ast.Node):
-                        node = result
-
-                    else:
-                        path_pop()
-                        continue
-
-        if result is None and is_edited:
-            edits.append((key, node))
-
-        if not is_leaving:
-            stack = Stack(in_array, index, keys, edits, stack)
-            in_array = isinstance(node, list)
-            keys = (  # type: ignore
-                node
-                if in_array
-                else visitor_keys.get(type(node), None) or []  # type: ignore
-            )
-            index = -1
-            edits = []
-
-            if parent:
-                ancestors_append(parent)
-
-            parent = node
-
-        if not stack:
-            break
-
-    if edits:
-        new_root = edits[-1][1]
-
-    return new_root
-
-
-@six.add_metaclass(VisitorMeta)
-class Visitor(object):
-    __slots__ = ()
-
-    def enter(
-        self,
-        node,  # type: Any
-        key,  # type: Union[None, int, str]
-        parent,  # type: Any
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> Any
-        method = self._get_enter_handler(type(node))  # type: ignore
-        if method:
-            return method(self, node, key, parent, path, ancestors)
-        return None
-
-    def leave(
-        self,
-        node,  # type: Any
-        key,  # type: Union[None, int, str]
-        parent,  # type: Any
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> Any
-        method = self._get_leave_handler(type(node))  # type: ignore
-        if method:
-            return method(self, node, key, parent, path, ancestors)
-        return None
-
-
-class ParallelVisitor(Visitor):
-    __slots__ = "skipping", "visitors"
-
-    def __init__(self, visitors):
-        # type: (List[Any]) -> None
-        self.visitors = visitors
-        self.skipping = [None] * len(
-            visitors
-        )  # type: List[Union[ast.Node, _Break, _Falsey, None]]
-        return None
-
-    def enter(
-        self,
-        node,  # type: Any
-        key,  # type: Union[None, int, str]
-        parent,  # type: Any
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> Any
-        for i, visitor in enumerate(self.visitors):
-            if not self.skipping[i]:
-                result = visitor.enter(node, key, parent, path, ancestors)
-                if result is False:
-                    self.skipping[i] = node
-                elif result is BREAK:
-                    self.skipping[i] = BREAK
-                elif result is not None:
-                    return result
-        return None
-
-    def leave(
-        self,
-        node,  # type: Any
-        key,  # type: Union[None, int, str]
-        parent,  # type: Any
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> Any
-        for i, visitor in enumerate(self.visitors):
-            if not self.skipping[i]:
-                result = visitor.leave(node, key, parent, path, ancestors)
-                if result is BREAK:
-                    self.skipping[i] = BREAK
-                elif result is not None and result is not False:
-                    return result
-            elif self.skipping[i] == node:
-                self.skipping[i] = REMOVE
-        return None
-
-
-class TypeInfoVisitor(Visitor):
-    __slots__ = "visitor", "type_info"
-
-    def __init__(self, type_info, visitor):
-        # type: (TypeInfo, Visitor) -> None
-        self.type_info = type_info
-        self.visitor = visitor
-
-    def enter(
-        self,
-        node,  # type: Any
-        key,  # type: Union[None, int, str]
-        parent,  # type: Any
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> Optional[Any]
-        self.type_info.enter(node)
-        result = self.visitor.enter(node, key, parent, path, ancestors)
-        if result is not None:
-            self.type_info.leave(node)
-            if isinstance(result, ast.Node):
-                self.type_info.enter(result)
-        return result
-
-    def leave(
-        self,
-        node,  # type: Any
-        key,  # type: Union[None, int, str]
-        parent,  # type: Any
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> Optional[Any]
-        result = self.visitor.leave(node, key, parent, path, ancestors)
-        self.type_info.leave(node)
-        return result
diff --git a/graphql/language/visitor_meta.py b/graphql/language/visitor_meta.py
deleted file mode 100644
index 37372c4..0000000
--- a/graphql/language/visitor_meta.py
+++ /dev/null
@@ -1,77 +0,0 @@
-from . import ast
-
-QUERY_DOCUMENT_KEYS = {
-    ast.Name: (),
-    ast.Document: ("definitions",),
-    ast.OperationDefinition: (
-        "name",
-        "variable_definitions",
-        "directives",
-        "selection_set",
-    ),
-    ast.VariableDefinition: ("variable", "type", "default_value"),
-    ast.Variable: ("name",),
-    ast.SelectionSet: ("selections",),
-    ast.Field: ("alias", "name", "arguments", "directives", "selection_set"),
-    ast.Argument: ("name", "value"),
-    ast.FragmentSpread: ("name", "directives"),
-    ast.InlineFragment: ("type_condition", "directives", "selection_set"),
-    ast.FragmentDefinition: ("name", "type_condition", "directives", "selection_set"),
-    ast.IntValue: (),
-    ast.FloatValue: (),
-    ast.StringValue: (),
-    ast.BooleanValue: (),
-    ast.EnumValue: (),
-    ast.ListValue: ("values",),
-    ast.ObjectValue: ("fields",),
-    ast.ObjectField: ("name", "value"),
-    ast.Directive: ("name", "arguments"),
-    ast.NamedType: ("name",),
-    ast.ListType: ("type",),
-    ast.NonNullType: ("type",),
-    ast.SchemaDefinition: ("directives", "operation_types"),
-    ast.OperationTypeDefinition: ("type",),
-    ast.ScalarTypeDefinition: ("name", "directives"),
-    ast.ObjectTypeDefinition: ("name", "interfaces", "directives", "fields"),
-    ast.FieldDefinition: ("name", "arguments", "directives", "type"),
-    ast.InputValueDefinition: ("name", "type", "directives", "default_value"),
-    ast.InterfaceTypeDefinition: ("name", "directives", "fields"),
-    ast.UnionTypeDefinition: ("name", "directives", "types"),
-    ast.EnumTypeDefinition: ("name", "directives", "values"),
-    ast.EnumValueDefinition: ("name", "directives"),
-    ast.InputObjectTypeDefinition: ("name", "directives", "fields"),
-    ast.TypeExtensionDefinition: ("definition",),
-    ast.DirectiveDefinition: ("name", "arguments", "locations"),
-}
-
-AST_KIND_TO_TYPE = {c.__name__: c for c in QUERY_DOCUMENT_KEYS.keys()}
-
-
-class VisitorMeta(type):
-    def __new__(cls, name, bases, attrs):
-        enter_handlers = {}
-        leave_handlers = {}
-
-        for base in bases:
-            if hasattr(base, "_enter_handlers"):
-                enter_handlers.update(base._enter_handlers)
-
-            if hasattr(base, "_leave_handlers"):
-                leave_handlers.update(base._leave_handlers)
-
-        for attr, val in attrs.items():
-            if attr.startswith("enter_"):
-                ast_kind = attr[6:]
-                ast_type = AST_KIND_TO_TYPE.get(ast_kind)
-                enter_handlers[ast_type] = val
-
-            elif attr.startswith("leave_"):
-                ast_kind = attr[6:]
-                ast_type = AST_KIND_TO_TYPE.get(ast_kind)
-                leave_handlers[ast_type] = val
-
-        attrs["_enter_handlers"] = enter_handlers
-        attrs["_leave_handlers"] = leave_handlers
-        attrs["_get_enter_handler"] = enter_handlers.get
-        attrs["_get_leave_handler"] = leave_handlers.get
-        return super(VisitorMeta, cls).__new__(cls, name, bases, attrs)
diff --git a/graphql/pyutils/__init__.py b/graphql/pyutils/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/pyutils/cached_property.py b/graphql/pyutils/cached_property.py
deleted file mode 100644
index c17a4db..0000000
--- a/graphql/pyutils/cached_property.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any
-
-
-class cached_property(object):
-    """ A property that is only computed once per instance and then replaces
-        itself with an ordinary attribute. Deleting the attribute resets the
-        property.
-
-        Source: https://github.com/bottlepy/bottle/commit/fa7733e075da0d790d809aa3d2f53071897e6f76
-        """
-
-    def __init__(self, func):
-        self.__doc__ = getattr(func, "__doc__")
-        self.func = func
-
-    def __get__(self, obj, cls):
-        # type: (Any, type) -> Any
-        if obj is None:
-            return self
-        value = obj.__dict__[self.func.__name__] = self.func(obj)
-        return value
diff --git a/graphql/pyutils/compat.py b/graphql/pyutils/compat.py
deleted file mode 100644
index 90769cb..0000000
--- a/graphql/pyutils/compat.py
+++ /dev/null
@@ -1,247 +0,0 @@
-# flake8: noqa
-
-# Copyright (c) 2010-2013 Benjamin Peterson
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy of
-# this software and associated documentation files (the "Software"), to deal in
-# the Software without restriction, including without limitation the rights to
-# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
-# the Software, and to permit persons to whom the Software is furnished to do so,
-# subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in all
-# copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
-# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
-# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
-# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
-# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-from __future__ import absolute_import
-
-import operator
-import sys
-import types
-
-try:
-    from enum import Enum
-except ImportError:
-    from .enum import Enum  # type: ignore
-
-if False:
-    from typing import Callable
-
-
-PY2 = sys.version_info[0] == 2
-PY3 = sys.version_info[0] == 3
-
-if PY3:
-    string_types = (str,)
-    integer_types = (int,)
-    class_types = (type,)
-    text_type = str
-    binary_type = bytes
-else:
-    string_types = (basestring,)
-    integer_types = (int, long)
-    class_types = (type, types.ClassType)
-    text_type = unicode
-    binary_type = str
-
-
-try:
-    advance_iterator = next
-except NameError:
-
-    def advance_iterator(it):  # type: ignore
-        return it.next()
-
-
-next = advance_iterator  # type: Callable
-
-
-try:
-    callable = callable  # type: Callable
-except NameError:
-
-    def callable(obj):  # type: ignore
-        return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
-
-
-if PY3:
-    Iterator = object
-else:
-
-    class Iterator(object):
-        def next(self):
-            return type(self).__next__(self)
-
-
-if PY3:
-
-    def iterkeys(d, **kw):
-        return iter(d.keys(**kw))
-
-    def itervalues(d, **kw):
-        return iter(d.values(**kw))
-
-    def iteritems(d, **kw):
-        return iter(d.items(**kw))
-
-    def iterlists(d, **kw):
-        return iter(d.lists(**kw))
-
-
-else:
-
-    def iterkeys(d, **kw):
-        return d.iterkeys(**kw)
-
-    def itervalues(d, **kw):
-        return d.itervalues(**kw)
-
-    def iteritems(d, **kw):
-        return d.iteritems(**kw)
-
-    def iterlists(d, **kw):
-        return d.iterlists(**kw)
-
-
-if PY3:
-
-    def b(s):
-        return s.encode("latin-1")
-
-    def u(s):
-        return s
-
-    import io
-
-    StringIO = io.StringIO
-    BytesIO = io.BytesIO
-else:
-
-    def b(s):
-        return s
-
-    # Workaround for standalone backslash
-
-    def u(s):
-        return unicode(s.replace(r"\\", r"\\\\"), "unicode_escape")
-
-    import StringIO
-
-    StringIO = BytesIO = StringIO.StringIO
-
-
-if PY3:
-    exec_ = getattr(__import__("builtins"), "exec")
-
-    def reraise(tp, value, tb=None):
-        try:
-            if value is None:
-                value = tp()
-            if value.__traceback__ is not tb:
-                raise value.with_traceback(tb)
-            raise value
-        finally:
-            value = None
-            tb = None
-
-
-else:
-
-    def exec_(_code_, _globs_=None, _locs_=None):
-        """Execute code in a namespace."""
-        if _globs_ is None:
-            frame = sys._getframe(1)
-            _globs_ = frame.f_globals
-            if _locs_ is None:
-                _locs_ = frame.f_locals
-            del frame
-        elif _locs_ is None:
-            _locs_ = _globs_
-        exec("""exec _code_ in _globs_, _locs_""")
-
-    exec_(
-        """def reraise(tp, value, tb=None):
-    try:
-        raise tp, value, tb
-    finally:
-        tb = None
-"""
-    )
-
-
-if sys.version_info[:2] == (3, 2):
-    exec_(
-        """def raise_from(value, from_value):
-    try:
-        if from_value is None:
-            raise value
-        raise value from from_value
-    finally:
-        value = None
-"""
-    )
-elif sys.version_info[:2] > (3, 2):
-    exec_(
-        """def raise_from(value, from_value):
-    try:
-        raise value from from_value
-    finally:
-        value = None
-"""
-    )
-else:
-
-    def raise_from(value, from_value):
-        raise value
-
-
-if PY3:
-    from urllib.error import HTTPError
-    from http import client as httplib
-    import urllib.request as urllib2
-    from queue import Queue
-    from urllib.parse import quote as urllib_quote
-    from urllib import parse as urlparse
-else:
-    from urllib2 import HTTPError
-    import httplib
-    import urllib2
-    from Queue import Queue
-    from urllib import quote as urllib_quote
-    import urlparse
-
-
-def get_code(func):
-    rv = getattr(func, "__code__", getattr(func, "func_code", None))
-    if rv is None:
-        raise TypeError("Could not get code from %r" % type(func).__name__)
-    return rv
-
-
-def check_threads():
-    try:
-        from uwsgi import opt
-    except ImportError:
-        return
-
-    # When `threads` is passed in as a uwsgi option,
-    # `enable-threads` is implied on.
-    if "threads" in opt:
-        return
-
-    if str(opt.get("enable-threads", "0")).lower() in ("false", "off", "no", "0"):
-        from warnings import warn
-
-        warn(
-            Warning(
-                "We detected the use of uwsgi with disabled threads.  "
-                "This will cause issues with the transport you are "
-                "trying to use.  Please enable threading for uwsgi.  "
-                '(Enable the "enable-threads" flag).'
-            )
-        )
diff --git a/graphql/pyutils/contain_subset.py b/graphql/pyutils/contain_subset.py
deleted file mode 100644
index 7c41ea5..0000000
--- a/graphql/pyutils/contain_subset.py
+++ /dev/null
@@ -1,35 +0,0 @@
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any
-
-obj = (dict, list, tuple)
-
-
-def contain_subset(expected, actual):
-    # type: (Any, Any) -> bool
-    t_actual = type(actual)
-    t_expected = type(expected)
-    actual_is_dict = issubclass(t_actual, dict)
-    expected_is_dict = issubclass(t_expected, dict)
-    both_dicts = actual_is_dict and expected_is_dict
-    if not (both_dicts) and not (
-        issubclass(t_actual, t_expected) or issubclass(t_expected, t_actual)
-    ):
-        return False
-    if not isinstance(expected, obj) or expected is None:
-        return expected == actual
-    if expected and not actual:
-        return False
-    if isinstance(expected, list):
-        aa = actual[:]
-        return all([any([contain_subset(exp, act) for act in aa]) for exp in expected])
-    for key in expected.keys():  # type: ignore
-        eo = expected[key]
-        ao = actual.get(key)
-        if isinstance(eo, obj) and eo is not None and ao is not None:
-            if not contain_subset(eo, ao):
-                return False
-            continue
-        if ao != eo:
-            return False
-    return True
diff --git a/graphql/pyutils/default_ordered_dict.py b/graphql/pyutils/default_ordered_dict.py
deleted file mode 100644
index 718b324..0000000
--- a/graphql/pyutils/default_ordered_dict.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import copy
-from collections import OrderedDict
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any
-
-
-class DefaultOrderedDict(OrderedDict):
-    __slots__ = ("default_factory",)
-
-    # Source: http://stackoverflow.com/a/6190500/562769
-    def __init__(self, default_factory=None, *a, **kw):
-        # type: (type, *Any, **Any) -> None
-        if default_factory is not None and not callable(default_factory):
-            raise TypeError("first argument must be callable")
-
-        OrderedDict.__init__(self, *a, **kw)
-        self.default_factory = default_factory
-
-    def __missing__(self, key):
-        # type: (str) -> Any
-        if self.default_factory is None:
-            raise KeyError(key)
-        self[key] = value = self.default_factory()
-        return value
-
-    def __reduce__(self):
-        if self.default_factory is None:
-            args = tuple()
-        else:
-            args = (self.default_factory,)
-        return type(self), args, None, None, iter(self.items())
-
-    def copy(self):
-        return self.__copy__()
-
-    def __copy__(self):
-        return type(self)(self.default_factory, self)
-
-    def __deepcopy__(self, memo):
-        return self.__class__(self.default_factory, copy.deepcopy(list(self.items())))
-
-    def __repr__(self):
-        return "DefaultOrderedDict({}, {})".format(
-            self.default_factory, OrderedDict.__repr__(self)[19:-1]
-        )
diff --git a/graphql/pyutils/enum.py b/graphql/pyutils/enum.py
deleted file mode 100644
index fdc08ae..0000000
--- a/graphql/pyutils/enum.py
+++ /dev/null
@@ -1,919 +0,0 @@
-# type: ignore
-"""Python Enumerations"""
-
-import sys as _sys
-
-__all__ = ["Enum", "IntEnum", "unique"]
-
-version = 1, 1, 6
-
-pyver = float("%s.%s" % _sys.version_info[:2])
-
-try:
-    any
-except NameError:
-
-    def any(iterable):
-        for element in iterable:
-            if element:
-                return True
-        return False
-
-
-try:
-    from collections import OrderedDict  # type: ignore
-except ImportError:
-
-    class OrderedDict(object):  # type: ignore
-        pass
-
-
-try:
-    basestring  # type: ignore
-except NameError:
-    # In Python 2 basestring is the ancestor of both str and unicode
-    # in Python 3 it's just str, but was missing in 3.1
-    basestring = str
-
-try:
-    unicode  # type: ignore
-except NameError:
-    # In Python 3 unicode no longer exists (it's just str)
-    unicode = str
-
-
-class _RouteClassAttributeToGetattr(object):
-    """Route attribute access on a class to __getattr__.
-
-    This is a descriptor, used to define attributes that act differently when
-    accessed through an instance and through a class.  Instance access remains
-    normal, but access to an attribute through a class will be routed to the
-    class's __getattr__ method; this is done by raising AttributeError.
-
-    """
-
-    def __init__(self, fget=None):
-        self.fget = fget
-
-    def __get__(self, instance, ownerclass=None):
-        if instance is None:
-            raise AttributeError()
-        return self.fget(instance)
-
-    def __set__(self, instance, value):
-        raise AttributeError("can't set attribute")
-
-    def __delete__(self, instance):
-        raise AttributeError("can't delete attribute")
-
-
-def _is_descriptor(obj):
-    """Returns True if obj is a descriptor, False otherwise."""
-    return (
-        hasattr(obj, "__get__") or hasattr(obj, "__set__") or hasattr(obj, "__delete__")
-    )
-
-
-def _is_dunder(name):
-    """Returns True if a __dunder__ name, False otherwise."""
-    return (
-        len(name) > 4
-        and name[:2] == name[-2:] == "__"
-        and name[2:3] != "_"
-        and name[-3:-2] != "_"
-    )
-
-
-def _is_sunder(name):
-    """Returns True if a _sunder_ name, False otherwise."""
-    return (
-        len(name) > 2
-        and name[0] == name[-1] == "_"
-        and name[1:2] != "_"
-        and name[-2:-1] != "_"
-    )
-
-
-def _make_class_unpicklable(cls):
-    """Make the given class un-picklable."""
-
-    def _break_on_call_reduce(self, protocol=None):
-        raise TypeError("%r cannot be pickled" % self)
-
-    cls.__reduce_ex__ = _break_on_call_reduce
-    cls.__module__ = "<unknown>"
-
-
-class _EnumDict(OrderedDict):
-    """Track enum member order and ensure member names are not reused.
-
-    EnumMeta will use the names found in self._member_names as the
-    enumeration member names.
-
-    """
-
-    def __init__(self):
-        super(_EnumDict, self).__init__()
-        self._member_names = []
-
-    def __setitem__(self, key, value):
-        """Changes anything not dundered or not a descriptor.
-
-        If a descriptor is added with the same name as an enum member, the name
-        is removed from _member_names (this may leave a hole in the numerical
-        sequence of values).
-
-        If an enum member name is used twice, an error is raised; duplicate
-        values are not checked for.
-
-        Single underscore (sunder) names are reserved.
-
-        Note:   in 3.x __order__ is simply discarded as a not necessary piece
-                leftover from 2.x
-
-        """
-        if pyver >= 3.0 and key in ("_order_", "__order__"):
-            return
-        elif key == "__order__":
-            key = "_order_"
-        if _is_sunder(key):
-            if key != "_order_":
-                raise ValueError("_names_ are reserved for future Enum use")
-        elif _is_dunder(key):
-            pass
-        elif key in self._member_names:
-            # descriptor overwriting an enum?
-            raise TypeError("Attempted to reuse key: %r" % key)
-        elif not _is_descriptor(value):
-            if key in self:
-                # enum overwriting a descriptor?
-                raise TypeError("Key already defined as: %r" % self[key])
-            self._member_names.append(key)
-        super(_EnumDict, self).__setitem__(key, value)
-
-
-# Dummy value for Enum as EnumMeta explicity checks for it, but of course until
-# EnumMeta finishes running the first time the Enum class doesn't exist.  This
-# is also why there are checks in EnumMeta like `if Enum is not None`
-Enum = None
-
-
-class EnumMeta(type):
-    """Metaclass for Enum"""
-
-    @classmethod
-    def __prepare__(metacls, cls, bases):
-        return _EnumDict()
-
-    def __new__(metacls, cls, bases, classdict):
-        # an Enum class is final once enumeration items have been defined; it
-        # cannot be mixed with other types (int, float, etc.) if it has an
-        # inherited __new__ unless a new __new__ is defined (or the resulting
-        # class will fail).
-        if isinstance(classdict, dict):
-            original_dict = classdict
-            classdict = _EnumDict()
-            for k, v in original_dict.items():
-                classdict[k] = v
-
-        member_type, first_enum = metacls._get_mixins_(bases)
-        __new__, save_new, use_args = metacls._find_new_(
-            classdict, member_type, first_enum
-        )
-        # save enum items into separate mapping so they don't get baked into
-        # the new class
-        members = {k: classdict[k] for k in classdict._member_names}
-        for name in classdict._member_names:
-            del classdict[name]
-
-        # py2 support for definition order
-        _order_ = classdict.get("_order_")
-        if _order_ is None:
-            if pyver < 3.0:
-                try:
-                    _order_ = [
-                        name
-                        for (name, value) in sorted(
-                            members.items(), key=lambda item: item[1]
-                        )
-                    ]
-                except TypeError:
-                    _order_ = [name for name in sorted(members.keys())]
-            else:
-                _order_ = classdict._member_names
-        else:
-            del classdict["_order_"]
-            if pyver < 3.0:
-                _order_ = _order_.replace(",", " ").split()
-                aliases = [name for name in members if name not in _order_]
-                _order_ += aliases
-
-        # check for illegal enum names (any others?)
-        invalid_names = set(members) & {"mro"}
-        if invalid_names:
-            raise ValueError(
-                "Invalid enum member name(s): {}".format(", ".join(invalid_names))
-            )
-
-        # save attributes from super classes so we know if we can take
-        # the shortcut of storing members in the class dict
-        base_attributes = {a for b in bases for a in b.__dict__}
-        # create our new Enum type
-        enum_class = super(EnumMeta, metacls).__new__(metacls, cls, bases, classdict)
-        enum_class._member_names_ = []  # names in random order
-        if OrderedDict is not None:
-            enum_class._member_map_ = OrderedDict()
-        else:
-            enum_class._member_map_ = {}  # name->value map
-        enum_class._member_type_ = member_type
-
-        # Reverse value->name map for hashable values.
-        enum_class._value2member_map_ = {}
-
-        # instantiate them, checking for duplicates as we go
-        # we instantiate first instead of checking for duplicates first in case
-        # a custom __new__ is doing something funky with the values -- such as
-        # auto-numbering ;)
-        if __new__ is None:
-            __new__ = enum_class.__new__
-        for member_name in _order_:
-            value = members[member_name]
-            if not isinstance(value, tuple):
-                args = (value,)
-            else:
-                args = value
-            if member_type is tuple:  # special case for tuple enums
-                args = (args,)  # wrap it one more time
-            if not use_args or not args:
-                enum_member = __new__(enum_class)
-                if not hasattr(enum_member, "_value_"):
-                    enum_member._value_ = value
-            else:
-                enum_member = __new__(enum_class, *args)
-                if not hasattr(enum_member, "_value_"):
-                    enum_member._value_ = member_type(*args)
-            value = enum_member._value_
-            enum_member._name_ = member_name
-            enum_member.__objclass__ = enum_class
-            enum_member.__init__(*args)
-            # If another member with the same value was already defined, the
-            # new member becomes an alias to the existing one.
-            for name, canonical_member in enum_class._member_map_.items():
-                if canonical_member.value == enum_member._value_:
-                    enum_member = canonical_member
-                    break
-            else:
-                # Aliases don't appear in member names (only in __members__).
-                enum_class._member_names_.append(member_name)
-            # performance boost for any member that would not shadow
-            # a DynamicClassAttribute (aka _RouteClassAttributeToGetattr)
-            if member_name not in base_attributes:
-                setattr(enum_class, member_name, enum_member)
-            # now add to _member_map_
-            enum_class._member_map_[member_name] = enum_member
-            try:
-                # This may fail if value is not hashable. We can't add the value
-                # to the map, and by-value lookups for this value will be
-                # linear.
-                enum_class._value2member_map_[value] = enum_member
-            except TypeError:
-                pass
-
-        # If a custom type is mixed into the Enum, and it does not know how
-        # to pickle itself, pickle.dumps will succeed but pickle.loads will
-        # fail.  Rather than have the error show up later and possibly far
-        # from the source, sabotage the pickle protocol for this class so
-        # that pickle.dumps also fails.
-        #
-        # However, if the new class implements its own __reduce_ex__, do not
-        # sabotage -- it's on them to make sure it works correctly.  We use
-        # __reduce_ex__ instead of any of the others as it is preferred by
-        # pickle over __reduce__, and it handles all pickle protocols.
-        unpicklable = False
-        if "__reduce_ex__" not in classdict:
-            if member_type is not object:
-                methods = (
-                    "__getnewargs_ex__",
-                    "__getnewargs__",
-                    "__reduce_ex__",
-                    "__reduce__",
-                )
-                if not any(m in member_type.__dict__ for m in methods):
-                    _make_class_unpicklable(enum_class)
-                    unpicklable = True
-
-        # double check that repr and friends are not the mixin's or various
-        # things break (such as pickle)
-        for name in ("__repr__", "__str__", "__format__", "__reduce_ex__"):
-            class_method = getattr(enum_class, name)
-            getattr(member_type, name, None)
-            enum_method = getattr(first_enum, name, None)
-            if name not in classdict and class_method is not enum_method:
-                if name == "__reduce_ex__" and unpicklable:
-                    continue
-                setattr(enum_class, name, enum_method)
-
-        # method resolution and int's are not playing nice
-        # Python's less than 2.6 use __cmp__
-
-        if pyver < 2.6:
-
-            if issubclass(enum_class, int):
-                setattr(enum_class, "__cmp__", getattr(int, "__cmp__"))
-
-        elif pyver < 3.0:
-
-            if issubclass(enum_class, int):
-                for method in (
-                    "__le__",
-                    "__lt__",
-                    "__gt__",
-                    "__ge__",
-                    "__eq__",
-                    "__ne__",
-                    "__hash__",
-                ):
-                    setattr(enum_class, method, getattr(int, method))
-
-        # replace any other __new__ with our own (as long as Enum is not None,
-        # anyway) -- again, this is to support pickle
-        if Enum is not None:
-            # if the user defined their own __new__, save it before it gets
-            # clobbered in case they subclass later
-            if save_new:
-                setattr(enum_class, "__member_new__", enum_class.__dict__["__new__"])
-            setattr(enum_class, "__new__", Enum.__dict__["__new__"])
-        return enum_class
-
-    def __bool__(cls):
-        """
-        classes/types should always be True.
-        """
-        return True
-
-    def __call__(cls, value, names=None, module=None, type=None, start=1):
-        """Either returns an existing member, or creates a new enum class.
-
-        This method is used both when an enum class is given a value to match
-        to an enumeration member (i.e. Color(3)) and for the functional API
-        (i.e. Color = Enum('Color', names='red green blue')).
-
-        When used for the functional API: `module`, if set, will be stored in
-        the new class' __module__ attribute; `type`, if set, will be mixed in
-        as the first base class.
-
-        Note: if `module` is not set this routine will attempt to discover the
-        calling module by walking the frame stack; if this is unsuccessful
-        the resulting class will not be pickleable.
-
-        """
-        if names is None:  # simple value lookup
-            return cls.__new__(cls, value)
-        # otherwise, functional API: we're creating a new Enum type
-        return cls._create_(value, names, module=module, type=type, start=start)
-
-    def __contains__(cls, member):
-        return isinstance(member, cls) and member.name in cls._member_map_
-
-    def __delattr__(cls, attr):
-        # nicer error message when someone tries to delete an attribute
-        # (see issue19025).
-        if attr in cls._member_map_:
-            raise AttributeError("%s: cannot delete Enum member." % cls.__name__)
-        super(EnumMeta, cls).__delattr__(attr)
-
-    def __dir__(self):
-        return [
-            "__class__",
-            "__doc__",
-            "__members__",
-            "__module__",
-        ] + self._member_names_
-
-    @property
-    def __members__(cls):
-        """Returns a mapping of member name->value.
-
-        This mapping lists all enum members, including aliases. Note that this
-        is a copy of the internal mapping.
-
-        """
-        return cls._member_map_.copy()
-
-    def __getattr__(cls, name):
-        """Return the enum member matching `name`
-
-        We use __getattr__ instead of descriptors or inserting into the enum
-        class' __dict__ in order to support `name` and `value` being both
-        properties for enum members (which live in the class' __dict__) and
-        enum members themselves.
-
-        """
-        if _is_dunder(name):
-            raise AttributeError(name)
-        try:
-            return cls._member_map_[name]
-        except KeyError:
-            raise AttributeError(name)
-
-    def __getitem__(cls, name):
-        return cls._member_map_[name]
-
-    def __iter__(cls):
-        return (cls._member_map_[name] for name in cls._member_names_)
-
-    def __reversed__(cls):
-        return (cls._member_map_[name] for name in reversed(cls._member_names_))
-
-    def __len__(cls):
-        return len(cls._member_names_)
-
-    __nonzero__ = __bool__
-
-    def __repr__(cls):
-        return "<enum %r>" % cls.__name__
-
-    def __setattr__(cls, name, value):
-        """Block attempts to reassign Enum members.
-
-        A simple assignment to the class namespace only changes one of the
-        several possible ways to get an Enum member from the Enum class,
-        resulting in an inconsistent Enumeration.
-
-        """
-        member_map = cls.__dict__.get("_member_map_", {})
-        if name in member_map:
-            raise AttributeError("Cannot reassign members.")
-        super(EnumMeta, cls).__setattr__(name, value)
-
-    def _create_(cls, class_name, names=None, module=None, type=None, start=1):
-        """Convenience method to create a new Enum class.
-
-        `names` can be:
-
-        * A string containing member names, separated either with spaces or
-          commas.  Values are auto-numbered from 1.
-        * An iterable of member names.  Values are auto-numbered from 1.
-        * An iterable of (member name, value) pairs.
-        * A mapping of member name -> value.
-
-        """
-        if pyver < 3.0:
-            # if class_name is unicode, attempt a conversion to ASCII
-            if isinstance(class_name, unicode):
-                try:
-                    class_name = class_name.encode("ascii")
-                except UnicodeEncodeError:
-                    raise TypeError("%r is not representable in ASCII" % class_name)
-        metacls = cls.__class__
-        if type is None:
-            bases = (cls,)
-        else:
-            bases = (type, cls)
-        classdict = metacls.__prepare__(class_name, bases)
-        _order_ = []
-
-        # special processing needed for names?
-        if isinstance(names, basestring):
-            names = names.replace(",", " ").split()
-        if isinstance(names, (tuple, list)) and isinstance(names[0], basestring):
-            names = [(e, i + start) for (i, e) in enumerate(names)]
-
-        # Here, names is either an iterable of (name, value) or a mapping.
-        item = None  # in case names is empty
-        for item in names:
-            if isinstance(item, basestring):
-                member_name, member_value = item, names[item]
-            else:
-                member_name, member_value = item
-            classdict[member_name] = member_value
-            _order_.append(member_name)
-        # only set _order_ in classdict if name/value was not from a mapping
-        if not isinstance(item, basestring):
-            classdict["_order_"] = " ".join(_order_)
-        enum_class = metacls.__new__(metacls, class_name, bases, classdict)
-
-        # TODO: replace the frame hack if a blessed way to know the calling
-        # module is ever developed
-        if module is None:
-            try:
-                module = _sys._getframe(2).f_globals["__name__"]
-            except (AttributeError, ValueError):
-                pass
-        if module is None:
-            _make_class_unpicklable(enum_class)
-        else:
-            enum_class.__module__ = module
-
-        return enum_class
-
-    @staticmethod
-    def _get_mixins_(bases):
-        """Returns the type for creating enum members, and the first inherited
-        enum class.
-
-        bases: the tuple of bases that was given to __new__
-
-        """
-        if not bases or Enum is None:
-            return object, Enum
-
-        # double check that we are not subclassing a class with existing
-        # enumeration members; while we're at it, see if any other data
-        # type has been mixed in so we can use the correct __new__
-        member_type = first_enum = None
-        for base in bases:
-            if base is not Enum and issubclass(base, Enum) and base._member_names_:
-                raise TypeError("Cannot extend enumerations")
-        # base is now the last base in bases
-        if not issubclass(base, Enum):
-            raise TypeError(
-                "new enumerations must be created as "
-                "`ClassName([mixin_type,] enum_type)`"
-            )
-
-        # get correct mix-in type (either mix-in type of Enum subclass, or
-        # first base if last base is Enum)
-        if not issubclass(bases[0], Enum):
-            member_type = bases[0]  # first data type
-            first_enum = bases[-1]  # enum type
-        else:
-            for base in bases[0].__mro__:
-                # most common: (IntEnum, int, Enum, object)
-                # possible:    (<Enum 'AutoIntEnum'>, <Enum 'IntEnum'>,
-                #               <class 'int'>, <Enum 'Enum'>,
-                #               <class 'object'>)
-                if issubclass(base, Enum):
-                    if first_enum is None:
-                        first_enum = base
-                else:
-                    if member_type is None:
-                        member_type = base
-
-        return member_type, first_enum
-
-    if pyver < 3.0:
-
-        @staticmethod
-        def _find_new_(classdict, member_type, first_enum):
-            """Returns the __new__ to be used for creating the enum members.
-
-            classdict: the class dictionary given to __new__
-            member_type: the data type whose __new__ will be used by default
-            first_enum: enumeration to check for an overriding __new__
-
-            """
-            # now find the correct __new__, checking to see of one was defined
-            # by the user; also check earlier enum classes in case a __new__ was
-            # saved as __member_new__
-            __new__ = classdict.get("__new__", None)
-            if __new__:
-                return None, True, True  # __new__, save_new, use_args
-
-            N__new__ = getattr(None, "__new__")
-            O__new__ = getattr(object, "__new__")
-            if Enum is None:
-                E__new__ = N__new__
-            else:
-                E__new__ = Enum.__dict__["__new__"]
-            # check all possibles for __member_new__ before falling back to
-            # __new__
-            for method in ("__member_new__", "__new__"):
-                for possible in (member_type, first_enum):
-                    try:
-                        target = possible.__dict__[method]
-                    except (AttributeError, KeyError):
-                        target = getattr(possible, method, None)
-                    if target not in [None, N__new__, O__new__, E__new__]:
-                        if method == "__member_new__":
-                            classdict["__new__"] = target
-                            return None, False, True
-                        if isinstance(target, staticmethod):
-                            target = target.__get__(member_type)
-                        __new__ = target
-                        break
-                if __new__ is not None:
-                    break
-            else:
-                __new__ = object.__new__
-
-            # if a non-object.__new__ is used then whatever value/tuple was
-            # assigned to the enum member name will be passed to __new__ and to the
-            # new enum member's __init__
-            if __new__ is object.__new__:
-                use_args = False
-            else:
-                use_args = True
-
-            return __new__, False, use_args
-
-    else:
-
-        @staticmethod
-        def _find_new_(classdict, member_type, first_enum):
-            """Returns the __new__ to be used for creating the enum members.
-
-            classdict: the class dictionary given to __new__
-            member_type: the data type whose __new__ will be used by default
-            first_enum: enumeration to check for an overriding __new__
-
-            """
-            # now find the correct __new__, checking to see of one was defined
-            # by the user; also check earlier enum classes in case a __new__ was
-            # saved as __member_new__
-            __new__ = classdict.get("__new__", None)
-
-            # should __new__ be saved as __member_new__ later?
-            save_new = __new__ is not None
-
-            if __new__ is None:
-                # check all possibles for __member_new__ before falling back to
-                # __new__
-                for method in ("__member_new__", "__new__"):
-                    for possible in (member_type, first_enum):
-                        target = getattr(possible, method, None)
-                        if target not in (
-                            None,
-                            None.__new__,
-                            object.__new__,
-                            Enum.__new__,
-                        ):
-                            __new__ = target
-                            break
-                    if __new__ is not None:
-                        break
-                else:
-                    __new__ = object.__new__
-
-            # if a non-object.__new__ is used then whatever value/tuple was
-            # assigned to the enum member name will be passed to __new__ and to the
-            # new enum member's __init__
-            if __new__ is object.__new__:
-                use_args = False
-            else:
-                use_args = True
-
-            return __new__, save_new, use_args
-
-
-########################################################
-# In order to support Python 2 and 3 with a single
-# codebase we have to create the Enum methods separately
-# and then use the `type(name, bases, dict)` method to
-# create the class.
-########################################################
-temp_enum_dict = {}
-temp_enum_dict[
-    "__doc__"
-] = "Generic enumeration.\n\n    Derive from this class to define new enumerations.\n\n"
-
-
-def __new__(cls, value):
-    # all enum instances are actually created during class construction
-    # without calling this method; this method is called by the metaclass'
-    # __call__ (i.e. Color(3) ), and by pickle
-    if isinstance(value, cls):
-        # For lookups like Color(Color.red)
-        value = value.value
-        # return value
-    # by-value search for a matching enum member
-    # see if it's in the reverse mapping (for hashable values)
-    try:
-        if value in cls._value2member_map_:
-            return cls._value2member_map_[value]
-    except TypeError:
-        # not there, now do long search -- O(n) behavior
-        for member in cls._member_map_.values():
-            if member.value == value:
-                return member
-    raise ValueError("{} is not a valid {}".format(value, cls.__name__))
-
-
-temp_enum_dict["__new__"] = __new__  # type: ignore
-del __new__
-
-
-def __repr__(self):
-    return "<{}.{}: {!r}>".format(self.__class__.__name__, self._name_, self._value_)
-
-
-temp_enum_dict["__repr__"] = __repr__  # type: ignore
-del __repr__
-
-
-def __str__(self):
-    return "{}.{}".format(self.__class__.__name__, self._name_)
-
-
-temp_enum_dict["__str__"] = __str__  # type: ignore
-del __str__
-
-if pyver >= 3.0:
-
-    def __dir__(self):
-        added_behavior = [
-            m
-            for cls in self.__class__.mro()
-            for m in cls.__dict__
-            if m[0] != "_" and m not in self._member_map_
-        ]
-        return ["__class__", "__doc__", "__module__"] + added_behavior
-
-    temp_enum_dict["__dir__"] = __dir__  # type: ignore
-    del __dir__
-
-
-def __format__(self, format_spec):
-    # mixed-in Enums should use the mixed-in type's __format__, otherwise
-    # we can get strange results with the Enum name showing up instead of
-    # the value
-
-    # pure Enum branch
-    if self._member_type_ is object:
-        cls = str
-        val = str(self)
-    # mix-in branch
-    else:
-        cls = self._member_type_
-        val = self.value
-    return cls.__format__(val, format_spec)
-
-
-temp_enum_dict["__format__"] = __format__  # type: ignore
-del __format__
-
-
-####################################
-# Python's less than 2.6 use __cmp__
-
-if pyver < 2.6:
-
-    def __cmp__(self, other):
-        if isinstance(other, self.__class__):
-            if self is other:
-                return 0
-            return -1
-        return NotImplemented
-        raise TypeError(
-            "unorderable types: %s() and %s()"
-            % (self.__class__.__name__, other.__class__.__name__)
-        )
-
-    temp_enum_dict["__cmp__"] = __cmp__  # type: ignore
-    del __cmp__
-
-else:
-
-    def __le__(self, other):
-        raise TypeError(
-            "unorderable types: %s() <= %s()"
-            % (self.__class__.__name__, other.__class__.__name__)
-        )
-
-    temp_enum_dict["__le__"] = __le__  # type: ignore
-    del __le__
-
-    def __lt__(self, other):
-        raise TypeError(
-            "unorderable types: %s() < %s()"
-            % (self.__class__.__name__, other.__class__.__name__)
-        )
-
-    temp_enum_dict["__lt__"] = __lt__  # type: ignore
-    del __lt__
-
-    def __ge__(self, other):
-        raise TypeError(
-            "unorderable types: %s() >= %s()"
-            % (self.__class__.__name__, other.__class__.__name__)
-        )
-
-    temp_enum_dict["__ge__"] = __ge__  # type: ignore
-    del __ge__
-
-    def __gt__(self, other):
-        raise TypeError(
-            "unorderable types: %s() > %s()"
-            % (self.__class__.__name__, other.__class__.__name__)
-        )
-
-    temp_enum_dict["__gt__"] = __gt__  # type: ignore
-    del __gt__
-
-
-def __eq__(self, other):
-    if isinstance(other, self.__class__):
-        return self is other
-    return NotImplemented
-
-
-temp_enum_dict["__eq__"] = __eq__  # type: ignore
-del __eq__
-
-
-def __ne__(self, other):
-    if isinstance(other, self.__class__):
-        return self is not other
-    return NotImplemented
-
-
-temp_enum_dict["__ne__"] = __ne__  # type: ignore
-del __ne__
-
-
-def __hash__(self):
-    return hash(self._name_)
-
-
-temp_enum_dict["__hash__"] = __hash__  # type: ignore
-del __hash__
-
-
-def __reduce_ex__(self, proto):
-    return self.__class__, (self._value_,)
-
-
-temp_enum_dict["__reduce_ex__"] = __reduce_ex__  # type: ignore
-del __reduce_ex__
-
-# _RouteClassAttributeToGetattr is used to provide access to the `name`
-# and `value` properties of enum members while keeping some measure of
-# protection from modification, while still allowing for an enumeration
-# to have members named `name` and `value`.  This works because enumeration
-# members are not set directly on the enum class -- __getattr__ is
-# used to look them up.
-
-
-@_RouteClassAttributeToGetattr
-def name(self):
-    return self._name_
-
-
-temp_enum_dict["name"] = name  # type: ignore
-del name
-
-
-@_RouteClassAttributeToGetattr
-def value(self):
-    return self._value_
-
-
-temp_enum_dict["value"] = value  # type: ignore
-del value
-
-
-@classmethod  # type: ignore
-def _convert(cls, name, module, filter, source=None):
-    """
-    Create a new Enum subclass that replaces a collection of global constants
-    """
-    # convert all constants from source (or module) that pass filter() to
-    # a new Enum called name, and export the enum and its members back to
-    # module;
-    # also, replace the __reduce_ex__ method so unpickling works in
-    # previous Python versions
-    module_globals = vars(_sys.modules[module])
-    if source:
-        source = vars(source)
-    else:
-        source = module_globals
-    members = {name: value for name, value in source.items() if filter(name)}
-    cls = cls(name, members, module=module)
-    cls.__reduce_ex__ = _reduce_ex_by_name
-    module_globals.update(cls.__members__)
-    module_globals[name] = cls
-    return cls
-
-
-temp_enum_dict["_convert"] = _convert  # type: ignore
-del _convert
-
-Enum = EnumMeta("Enum", (object,), temp_enum_dict)
-del temp_enum_dict
-
-# Enum has now been created
-###########################
-
-
-class IntEnum(int, Enum):  # type: ignore
-    """Enum where members are also (and must be) ints"""
-
-
-def _reduce_ex_by_name(self, proto):
-    return self.name
-
-
-def unique(enumeration):
-    """Class decorator that ensures only unique members exist in an enumeration."""
-    duplicates = []
-    for name, member in enumeration.__members__.items():
-        if name != member.name:
-            duplicates.append((name, member.name))
-    if duplicates:
-        duplicate_names = ", ".join(
-            ["{} -> {}".format(alias, name) for (alias, name) in duplicates]
-        )
-        raise ValueError(
-            "duplicate names found in {!r}: {}".format(enumeration, duplicate_names)
-        )
-    return enumeration
diff --git a/graphql/pyutils/ordereddict.py b/graphql/pyutils/ordereddict.py
deleted file mode 100644
index 5a6b2b6..0000000
--- a/graphql/pyutils/ordereddict.py
+++ /dev/null
@@ -1,8 +0,0 @@
-try:
-    # Try to load the Cython performant OrderedDict (C)
-    # as is more performant than collections.OrderedDict (Python)
-    from cyordereddict import OrderedDict  # type: ignore
-except ImportError:
-    from collections import OrderedDict
-
-__all__ = ["OrderedDict"]
diff --git a/graphql/pyutils/pair_set.py b/graphql/pyutils/pair_set.py
deleted file mode 100644
index e7b18eb..0000000
--- a/graphql/pyutils/pair_set.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Dict, Any
-
-
-class PairSet(object):
-    __slots__ = ("_data",)
-
-    def __init__(self):
-        # type: () -> None
-        self._data = {}  # type: Dict[str, Any]
-
-    def __contains__(self, item):
-        return self.has(item[0], item[1], item[2])
-
-    def __str__(self):
-        return str(self._data)
-
-    def __repr__(self):
-        return str(self._data)
-
-    def has(self, a, b, are_mutually_exclusive):
-        first = self._data.get(a)
-        result = first and first.get(b)
-        if result is None:
-            return False
-
-        # are_mutually_exclusive being false is a superset of being true,
-        # hence if we want to know if this PairSet "has" these two with no
-        # exclusivity, we have to ensure it was added as such.
-        if not are_mutually_exclusive:
-            return not result
-
-        return True
-
-    def add(self, a, b, are_mutually_exclusive):
-        _pair_set_add(self._data, a, b, are_mutually_exclusive)
-        _pair_set_add(self._data, b, a, are_mutually_exclusive)
-        return self
-
-
-def _pair_set_add(data, a, b, are_mutually_exclusive):
-    sub_dict = data.get(a)
-
-    if not sub_dict:
-        sub_dict = {}
-        data[a] = sub_dict
-
-    sub_dict[b] = are_mutually_exclusive
diff --git a/graphql/pyutils/tests/__init__.py b/graphql/pyutils/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/pyutils/tests/test_contain_subset.py b/graphql/pyutils/tests/test_contain_subset.py
deleted file mode 100644
index 361e152..0000000
--- a/graphql/pyutils/tests/test_contain_subset.py
+++ /dev/null
@@ -1,79 +0,0 @@
-from ..contain_subset import contain_subset
-
-plain_object = {"a": "b", "c": "d"}
-
-complex_object = {"a": "b", "c": "d", "e": {"foo": "bar", "baz": {"qux": "quux"}}}
-
-
-def test_plain_object_should_pass_for_smaller_object():
-    # type: () -> None
-    assert contain_subset({"a": "b"}, plain_object)
-
-
-def test_plain_object_should_pass_for_same_object():
-    # type: () -> None
-    assert contain_subset({"a": "b", "c": "d"}, plain_object)
-
-
-def test_plain_object_should_reject_for_similar_object():
-    # type: () -> None
-    assert not contain_subset({"a": "notB", "c": "d"}, plain_object)
-
-
-def test_complex_object_should_pass_for_smaller_object():
-    # type: () -> None
-    assert contain_subset({"a": "b", "e": {"foo": "bar"}}, complex_object)
-
-
-def test_complex_object_should_pass_for_smaller_object_other():
-    # type: () -> None
-    assert contain_subset({"e": {"foo": "bar", "baz": {"qux": "quux"}}}, complex_object)
-
-
-def test_complex_object_should_pass_for_same_object():
-    # type: () -> None
-    assert contain_subset(
-        {"a": "b", "c": "d", "e": {"foo": "bar", "baz": {"qux": "quux"}}},
-        complex_object,
-    )
-
-
-def test_complex_object_should_reject_for_similar_object():
-    # type: () -> None
-    assert not contain_subset(
-        {"e": {"foo": "bar", "baz": {"qux": "notAQuux"}}}, complex_object
-    )
-
-
-# def test_circular_objects_should_contain_subdocument():
-#     obj = {}
-#     obj["arr"] = [obj, obj]
-#     obj["arr"].append(obj["arr"])
-#     obj["obj"] = obj
-
-#     assert contain_subset(
-#         {"arr": [{"arr": []}, {"arr": []}, [{"arr": []}, {"arr": []}]]}, obj
-#     )
-
-
-# def test_circular_objects_should_not_contain_similardocument():
-#     obj = {}
-#     obj["arr"] = [obj, obj]
-#     obj["arr"].append(obj["arr"])
-#     obj["obj"] = obj
-
-#     assert not contain_subset(
-#         {
-#             "arr": [
-#                 {"arr": ["just random field"]},
-#                 {"arr": []},
-#                 [{"arr": []}, {"arr": []}],
-#             ]
-#         },
-#         obj,
-#     )
-
-
-def test_should_contain_others():
-    obj = {"elems": [{"a": "b", "c": "d", "e": "f"}, {"g": "h"}]}
-    assert contain_subset({"elems": [{"g": "h"}, {"a": "b", "e": "f"}]}, obj)
diff --git a/graphql/pyutils/tests/test_default_ordered_dict.py b/graphql/pyutils/tests/test_default_ordered_dict.py
deleted file mode 100644
index 0a87a8d..0000000
--- a/graphql/pyutils/tests/test_default_ordered_dict.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import copy
-import pickle
-
-from pytest import raises
-
-from graphql.pyutils.default_ordered_dict import DefaultOrderedDict
-
-
-def test_will_missing_will_set_value_from_factory():
-    d = DefaultOrderedDict(list)
-    f = d["foo"]
-    assert isinstance(f, list)
-    assert d["foo"] is f
-
-
-def test_preserves_input_order():
-    d = DefaultOrderedDict(list)
-    d["a"].append(1)
-    d["b"].append(2)
-    d["c"].append(3)
-    d["a"].append(4)
-
-    assert list(d.keys()) == ["a", "b", "c"]
-    assert list(d.values()) == [[1, 4], [2], [3]]
-
-
-def test_will_act_list_default_dict_if_no_factory_defined():
-    d = DefaultOrderedDict()
-
-    with raises(KeyError) as excinfo:
-        assert d["test"]
-
-    assert str(excinfo.value) == "'test'"
-
-
-def test_will_repr_properly():
-    d = DefaultOrderedDict(list, [("a", 1), ("b", 2)])
-    assert repr(d) == "DefaultOrderedDict({}, [('a', 1), ('b', 2)])".format(list)
-
-
-def test_requires_callable_default_factory():
-    with raises(TypeError) as excinfo:
-        DefaultOrderedDict("not callable")
-
-    assert str(excinfo.value) == "first argument must be callable"
-
-
-def test_picklable():
-    d = DefaultOrderedDict(list, [("a", 1), ("b", 2)])
-    d_copied = pickle.loads(pickle.dumps(d))
-
-    assert d_copied == d
-    assert d.default_factory == d_copied.default_factory
-
-    d = DefaultOrderedDict(None, [("a", 1), ("b", 2)])
-    d_copied = pickle.loads(pickle.dumps(d))
-
-    assert d_copied == d
-    assert d.default_factory == d_copied.default_factory
-
-
-def test_copy():
-    d = DefaultOrderedDict(list, [("a", [1, 2]), ("b", [3, 4])])
-    d_copied = copy.copy(d)
-
-    assert d_copied == d
-    assert d.default_factory == d_copied.default_factory
-    assert d_copied["a"] is d["a"]
-    assert d_copied["b"] is d["b"]
-
-    d_copied = d.copy()
-
-    assert d_copied == d
-    assert d.default_factory == d_copied.default_factory
-    assert d_copied["a"] is d["a"]
-    assert d_copied["b"] is d["b"]
-
-
-def test_deep_copy():
-    d = DefaultOrderedDict(list, [("a", [1, 2]), ("b", [3, 4])])
-    d_copied = copy.deepcopy(d)
-
-    assert d_copied == d
-    assert d.default_factory == d_copied.default_factory
-    assert d_copied["a"] == d["a"]
-    assert d_copied["a"] is not d["a"]
-    assert d_copied["b"] == d["b"]
-    assert d_copied["b"] is not d["b"]
diff --git a/graphql/pyutils/tests/test_enum.py b/graphql/pyutils/tests/test_enum.py
deleted file mode 100644
index 8e2755f..0000000
--- a/graphql/pyutils/tests/test_enum.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from ..enum import _is_dunder, _is_sunder
-
-
-def test__is_dunder():
-    dunder_names = ["__i__", "__test__"]
-    non_dunder_names = ["test", "__test", "_test", "_test_", "test__", ""]
-
-    for name in dunder_names:
-        assert _is_dunder(name) is True
-
-    for name in non_dunder_names:
-        assert _is_dunder(name) is False
-
-
-def test__is_sunder():
-    sunder_names = ["_i_", "_test_"]
-
-    non_sunder_names = ["__i__", "_i__", "__i_", ""]
-
-    for name in sunder_names:
-        assert _is_sunder(name) is True
-
-    for name in non_sunder_names:
-        assert _is_sunder(name) is False
diff --git a/graphql/pyutils/tests/test_pair_set.py b/graphql/pyutils/tests/test_pair_set.py
deleted file mode 100644
index 860a209..0000000
--- a/graphql/pyutils/tests/test_pair_set.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from graphql.pyutils.pair_set import PairSet
-
-
-def test_pair_set():
-    ps = PairSet()
-    are_mutually_exclusive = True
-
-    ps.add(1, 2, are_mutually_exclusive)
-    ps.add(2, 4, are_mutually_exclusive)
-
-    assert ps.has(1, 2, are_mutually_exclusive)
-    assert ps.has(2, 1, are_mutually_exclusive)
-    assert not ps.has(1, 2, not are_mutually_exclusive)
-    assert not ps.has(2, 1, not are_mutually_exclusive)
-
-    assert (1, 2, are_mutually_exclusive) in ps
-    assert (2, 1, are_mutually_exclusive) in ps
-    assert (1, 2, (not are_mutually_exclusive)) not in ps
-    assert (2, 1, (not are_mutually_exclusive)) not in ps
-
-    assert ps.has(4, 2, are_mutually_exclusive)
-    assert ps.has(2, 4, are_mutually_exclusive)
-
-    assert not ps.has(2, 3, are_mutually_exclusive)
-    assert not ps.has(1, 3, are_mutually_exclusive)
-
-    assert ps.has(4, 2, are_mutually_exclusive)
-    assert ps.has(2, 4, are_mutually_exclusive)
-
-
-def test_pair_set_not_mutually_exclusive():
-    ps = PairSet()
-    are_mutually_exclusive = False
-
-    ps.add(1, 2, are_mutually_exclusive)
-
-    assert ps.has(1, 2, are_mutually_exclusive)
-    assert ps.has(2, 1, are_mutually_exclusive)
-
-    assert ps.has(1, 2, not are_mutually_exclusive)
-    assert ps.has(2, 1, not are_mutually_exclusive)
diff --git a/graphql/pyutils/version.py b/graphql/pyutils/version.py
deleted file mode 100644
index dc97126..0000000
--- a/graphql/pyutils/version.py
+++ /dev/null
@@ -1,81 +0,0 @@
-from __future__ import unicode_literals
-
-import datetime
-import os
-import subprocess
-
-
-def get_version(version=None):
-    "Returns a PEP 440-compliant version number from VERSION."
-    version = get_complete_version(version)
-
-    # Now build the two parts of the version number:
-    # main = X.Y[.Z]
-    # sub = .devN - for pre-alpha releases
-    #     | {a|b|rc}N - for alpha, beta, and rc releases
-
-    main = get_main_version(version)
-
-    sub = ""
-    if version[3] == "alpha" and version[4] == 0:
-        git_changeset = get_git_changeset()
-        if git_changeset:
-            sub = ".dev%s" % git_changeset
-        else:
-            sub = ".dev"
-    elif version[3] != "final":
-        mapping = {"alpha": "a", "beta": "b", "rc": "rc"}
-        sub = mapping[version[3]] + str(version[4])
-
-    return str(main + sub)
-
-
-def get_main_version(version=None):
-    "Returns main version (X.Y[.Z]) from VERSION."
-    version = get_complete_version(version)
-    parts = 2 if version[2] == 0 else 3
-    return ".".join(str(x) for x in version[:parts])
-
-
-def get_complete_version(version=None):
-    """Returns a tuple of the graphql version. If version argument is non-empty,
-    then checks for correctness of the tuple provided.
-    """
-    if version is None:
-        from graphql import VERSION as version
-    else:
-        assert len(version) == 5
-        assert version[3] in ("alpha", "beta", "rc", "final")
-
-    return version
-
-
-def get_docs_version(version=None):
-    version = get_complete_version(version)
-    if version[3] != "final":
-        return "dev"
-    else:
-        return "%d.%d" % version[:2]
-
-
-def get_git_changeset():
-    """Returns a numeric identifier of the latest git changeset.
-    The result is the UTC timestamp of the changeset in YYYYMMDDHHMMSS format.
-    This value isn't guaranteed to be unique, but collisions are very unlikely,
-    so it's sufficient for generating the development version numbers.
-    """
-    repo_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
-    try:
-        git_log = subprocess.Popen(
-            "git log --pretty=format:%ct --quiet -1 HEAD",
-            stdout=subprocess.PIPE,
-            stderr=subprocess.PIPE,
-            shell=True,
-            cwd=repo_dir,
-            universal_newlines=True,
-        )
-        timestamp = git_log.communicate()[0]
-        timestamp = datetime.datetime.utcfromtimestamp(int(timestamp))
-    except Exception:
-        return None
-    return timestamp.strftime("%Y%m%d%H%M%S")
diff --git a/graphql/type/__init__.py b/graphql/type/__init__.py
deleted file mode 100644
index 41a1111..0000000
--- a/graphql/type/__init__.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# flake8: noqa
-from .definition import (  # no import order
-    GraphQLScalarType,
-    GraphQLObjectType,
-    GraphQLField,
-    GraphQLArgument,
-    GraphQLInterfaceType,
-    GraphQLUnionType,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLInputObjectType,
-    GraphQLInputObjectField,
-    GraphQLList,
-    GraphQLNonNull,
-    get_named_type,
-    is_abstract_type,
-    is_composite_type,
-    is_input_type,
-    is_leaf_type,
-    is_type,
-    get_nullable_type,
-    is_output_type,
-)
-from .directives import (
-    # "Enum" of Directive locations
-    DirectiveLocation,
-    # Directive definition
-    GraphQLDirective,
-    # Built-in directives defined by the Spec
-    specified_directives,
-    GraphQLSkipDirective,
-    GraphQLIncludeDirective,
-    GraphQLDeprecatedDirective,
-    # Constant Deprecation Reason
-    DEFAULT_DEPRECATION_REASON,
-)
-from .scalars import (  # no import order
-    GraphQLInt,
-    GraphQLFloat,
-    GraphQLString,
-    GraphQLBoolean,
-    GraphQLID,
-)
-from .schema import GraphQLSchema
-
-from .introspection import (
-    # "Enum" of Type Kinds
-    TypeKind,
-    # GraphQL Types for introspection.
-    __Schema,
-    __Directive,
-    __DirectiveLocation,
-    __Type,
-    __Field,
-    __InputValue,
-    __EnumValue,
-    __TypeKind,
-    # Meta-field definitions.
-    SchemaMetaFieldDef,
-    TypeMetaFieldDef,
-    TypeNameMetaFieldDef,
-)
diff --git a/graphql/type/definition.py b/graphql/type/definition.py
deleted file mode 100644
index 2f499ae..0000000
--- a/graphql/type/definition.py
+++ /dev/null
@@ -1,782 +0,0 @@
-import collections
-
-try:
-    from collections.abc import Hashable, Mapping
-except ImportError:  # Python < 3.3
-    from collections import Hashable, Mapping
-import copy
-
-from ..language import ast
-from ..pyutils.cached_property import cached_property
-from ..pyutils.ordereddict import OrderedDict
-from ..pyutils.compat import Enum as PyEnum
-from ..utils.assert_valid_name import assert_valid_name
-from ..utils.undefined import Undefined
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import List, Dict, Any, Callable, Optional, Union, Type
-
-
-def is_type(type):
-    # type: (Any) -> bool
-    return isinstance(
-        type,
-        (
-            GraphQLScalarType,
-            GraphQLObjectType,
-            GraphQLInterfaceType,
-            GraphQLUnionType,
-            GraphQLEnumType,
-            GraphQLInputObjectType,
-            GraphQLList,
-            GraphQLNonNull,
-        ),
-    )
-
-
-def is_input_type(type):
-    # type: (Any) -> bool
-    named_type = get_named_type(type)
-    return isinstance(
-        named_type, (GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType)
-    )
-
-
-def is_output_type(type):
-    # type: (Any) -> bool
-    named_type = get_named_type(type)
-    return isinstance(
-        named_type,
-        (
-            GraphQLScalarType,
-            GraphQLObjectType,
-            GraphQLInterfaceType,
-            GraphQLUnionType,
-            GraphQLEnumType,
-        ),
-    )
-
-
-def is_leaf_type(type):
-    # type: (Any) -> bool
-    return isinstance(type, (GraphQLScalarType, GraphQLEnumType))
-
-
-def is_composite_type(type):
-    # type: (Any) -> bool
-    named_type = get_named_type(type)
-    return isinstance(
-        named_type, (GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType)
-    )
-
-
-def is_abstract_type(type):
-    return isinstance(type, (GraphQLInterfaceType, GraphQLUnionType))
-
-
-def get_nullable_type(type):
-    if isinstance(type, GraphQLNonNull):
-        return type.of_type
-    return type
-
-
-def get_named_type(type):
-    # type: (Optional[GraphQLType]) -> Optional[GraphQLType]
-    unmodified_type = type
-    while isinstance(unmodified_type, (GraphQLList, GraphQLNonNull)):
-        unmodified_type = unmodified_type.of_type
-
-    return unmodified_type
-
-
-class GraphQLType(object):
-    pass
-
-
-class GraphQLNamedType(GraphQLType):
-    __slots__ = ("name",)
-
-    def __init__(self, name):
-        # type: (str) -> None
-        self.name = name
-
-    def __str__(self):
-        # type: () -> str
-        return self.name
-
-    def is_same_type(self, other):
-        # type: (Any) -> bool
-        return self.__class__ is other.__class__ and self.name == other.name
-
-
-def none_func(x):
-    None
-
-
-class GraphQLScalarType(GraphQLNamedType):
-    """Scalar Type Definition
-
-    The leaf values of any request and input values to arguments are
-    Scalars (or Enums) and are defined with a name and a series of coercion
-    functions used to ensure validity.
-
-    Example:
-
-        def coerce_odd(value):
-            if value % 2 == 1:
-                return value
-            return None
-
-        OddType = GraphQLScalarType(name='Odd', serialize=coerce_odd)
-    """
-
-    __slots__ = "name", "description", "serialize", "parse_value", "parse_literal"
-
-    def __init__(
-        self,
-        name,  # type: str
-        description=None,  # type: Optional[str]
-        serialize=None,  # type: Optional[Callable]
-        parse_value=None,  # type: Optional[Callable]
-        parse_literal=None,  # type: Optional[Callable]
-    ):
-        # type: (...) -> None
-        assert name, "Type must be named."
-        assert_valid_name(name)
-        self.name = name
-        self.description = description
-
-        assert callable(serialize), (
-            '{} must provide "serialize" function. If this custom Scalar is '
-            'also used as an input type, ensure "parse_value" and "parse_literal" '
-            "functions are also provided."
-        ).format(self)
-
-        if parse_value is not None or parse_literal is not None:
-            assert callable(parse_value) and callable(
-                parse_literal
-            ), '{} must provide both "parse_value" and "parse_literal" functions.'.format(
-                self
-            )
-
-        self.serialize = serialize
-        self.parse_value = parse_value or none_func
-        self.parse_literal = parse_literal or none_func
-
-    def __str__(self):
-        # type: () -> str
-        return self.name
-
-
-class GraphQLObjectType(GraphQLNamedType):
-    """Object Type Definition
-
-    Almost all of the GraphQL types you define will be object types.
-    Object types have a name, but most importantly describe their fields.
-
-    Example:
-
-        AddressType = GraphQLObjectType('Address', {
-            'street': GraphQLField(GraphQLString),
-            'number': GraphQLField(GraphQLInt),
-            'formatted': GraphQLField(GraphQLString,
-                resolver=lambda obj, info, **args: obj.number + ' ' + obj.street),
-        })
-
-    When two types need to refer to each other, or a type needs to refer to
-    itself in a field, you can use a static method to supply the fields
-    lazily.
-
-    Example:
-
-        PersonType = GraphQLObjectType('Person', lambda: {
-            'name': GraphQLField(GraphQLString),
-            'bestFriend': GraphQLField(PersonType)
-        })
-    """
-
-    def __init__(
-        self,
-        name,  # type: str
-        fields,  # type: Union[Callable[[], Dict[str, GraphQLField]], Dict[str, GraphQLField]]
-        interfaces=None,  # type: Optional[List[GraphQLInterfaceType]]
-        is_type_of=None,  # type: Optional[Callable]
-        description=None,  # type: Optional[Any]
-    ):
-        # type: (...) -> None
-        assert name, "Type must be named."
-        assert_valid_name(name)
-        self.name = name
-        self.description = description
-
-        if is_type_of is not None:
-            assert callable(
-                is_type_of
-            ), '{} must provide "is_type_of" as a function.'.format(self)
-
-        self.is_type_of = is_type_of
-        self._fields = fields
-        self._provided_interfaces = interfaces
-        self._interfaces = None
-
-    @cached_property
-    def fields(self):
-        # type: () -> Dict[str, GraphQLField]
-        return define_field_map(self, self._fields)
-
-    @cached_property
-    def interfaces(self):
-        # type: () -> List[GraphQLInterfaceType]
-        return define_interfaces(self, self._provided_interfaces)
-
-
-def define_field_map(
-    type,  # type: Union[GraphQLInterfaceType, GraphQLObjectType]
-    field_map,  # type: Union[Callable, Dict[str, GraphQLField], OrderedDict]
-):
-    # type: (...) -> OrderedDict
-    if callable(field_map):
-        field_map = field_map()
-
-    assert isinstance(field_map, Mapping) and len(field_map) > 0, (
-        "{} fields must be a mapping (dict / OrderedDict) with field names as keys or a "
-        "function which returns such a mapping."
-    ).format(type)
-
-    for field_name, field in field_map.items():
-        assert_valid_name(field_name)
-        assert isinstance(
-            field, GraphQLField
-        ), "{}.{} must be an instance of GraphQLField.".format(type, field_name)
-        field_args = getattr(field, "args", None)
-
-        if field_args:
-            assert isinstance(
-                field_args, Mapping
-            ), "{}.{} args must be a mapping (dict / OrderedDict) with argument names as keys.".format(
-                type, field_name
-            )
-
-            for arg_name, arg in field_args.items():
-                assert_valid_name(arg_name)
-
-    return OrderedDict(field_map)
-
-
-def define_interfaces(
-    type,  # type: GraphQLObjectType
-    interfaces,  # type: Optional[List[GraphQLInterfaceType]]
-):
-    # type: (...) -> List[GraphQLInterfaceType]
-    if callable(interfaces):
-        interfaces = interfaces()
-
-    if interfaces is None:
-        interfaces = []
-
-    assert isinstance(
-        interfaces, (list, tuple)
-    ), "{} interfaces must be a list/tuple or a function which returns a list/tuple.".format(
-        type
-    )
-
-    for interface in interfaces:
-        assert isinstance(
-            interface, GraphQLInterfaceType
-        ), "{} may only implement Interface types, it cannot implement: {}.".format(
-            type, interface
-        )
-
-        if not callable(interface.resolve_type):
-            assert callable(type.is_type_of), (
-                'Interface Type {} does not provide a "resolve_type" function '
-                'and implementing Type {} does not provide a "is_type_of" '
-                "function. There is no way to resolve this implementing type "
-                "during execution."
-            ).format(interface, type)
-
-    return interfaces
-
-
-class GraphQLField(object):
-    __slots__ = "type", "args", "resolver", "deprecation_reason", "description"
-
-    def __init__(
-        self,
-        type,  # type: Any
-        args=None,  # type: Optional[Dict[str, GraphQLArgument]]
-        resolver=None,  # type: Optional[Callable]
-        deprecation_reason=None,  # type: Optional[Any]
-        description=None,  # type: Optional[Any]
-    ):
-        # type: (...) -> None
-        self.type = type
-        self.args = args or OrderedDict()
-        self.resolver = resolver
-        self.deprecation_reason = deprecation_reason
-        self.description = description
-
-    def __eq__(self, other):
-        return self is other or (
-            isinstance(other, GraphQLField)
-            and self.type == other.type
-            and self.args == other.args
-            and self.resolver == other.resolver
-            and self.deprecation_reason == other.deprecation_reason
-            and self.description == other.description
-        )
-
-    def __hash__(self):
-        # type: () -> int
-        return id(self)
-
-    @property
-    def is_deprecated(self):
-        return bool(self.deprecation_reason)
-
-
-class GraphQLArgument(object):
-    __slots__ = "type", "default_value", "description", "out_name"
-
-    def __init__(
-        self,
-        type,  # type: Union[GraphQLInputObjectType, GraphQLNonNull, GraphQLList, GraphQLScalarType]
-        default_value=None,  # type: Optional[Any]
-        description=None,  # type: Optional[Any]
-        out_name=None,  # type: Optional[str]
-    ):
-        # type: (...) -> None
-        self.type = type
-        self.default_value = default_value
-        self.description = description
-        self.out_name = out_name
-
-    def __eq__(self, other):
-        return self is other or (
-            isinstance(other, GraphQLArgument)
-            and self.type == other.type
-            and self.default_value == other.default_value
-            and self.description == other.description
-            and self.out_name == other.out_name
-        )
-
-    def __hash__(self):
-        return id(self)
-
-
-class GraphQLInterfaceType(GraphQLNamedType):
-    """Interface Type Definition
-
-    When a field can return one of a heterogeneous set of types, a Interface type is used to describe what types are possible,
-    what fields are in common across all types, as well as a function to determine which type is actually used when the field is resolved.
-
-    Example:
-
-        EntityType = GraphQLInterfaceType(
-            name='Entity',
-            fields={
-                'name': GraphQLField(GraphQLString),
-            })
-    """
-
-    def __init__(
-        self,
-        name,  # type: str
-        fields=None,  # type: Union[Callable[[], Dict[str, GraphQLField]], Dict[str, GraphQLField]]
-        resolve_type=None,  # type: Optional[Callable]
-        description=None,  # type: Optional[Any]
-    ):
-        # type: (...) -> None
-        assert name, "Type must be named."
-        assert_valid_name(name)
-        self.name = name
-        self.description = description
-
-        if resolve_type is not None:
-            assert callable(
-                resolve_type
-            ), '{} must provide "resolve_type" as a function.'.format(self)
-
-        self.resolve_type = resolve_type
-        self._fields = fields
-
-    @cached_property
-    def fields(self):
-        # type: () -> Dict[str, GraphQLField]
-        return define_field_map(self, self._fields)
-
-
-class GraphQLUnionType(GraphQLNamedType):
-    """Union Type Definition
-
-    When a field can return one of a heterogeneous set of types, a Union type is used to describe what types are possible
-    as well as providing a function to determine which type is actually used when the field is resolved.
-
-    Example:
-
-        class PetType(GraphQLUnionType):
-            name = 'Pet'
-            types = [DogType, CatType]
-
-            def resolve_type(self, value):
-                if isinstance(value, Dog):
-                    return DogType()
-                if isinstance(value, Cat):
-                    return CatType()
-    """
-
-    def __init__(
-        self,
-        name,  # type: str
-        types,  # type: Union[Callable[[], List[GraphQLObjectType]], List[GraphQLObjectType]]
-        resolve_type=None,  # type: Optional[Callable]
-        description=None,  # type: Optional[Any]
-    ):
-        # type: (...) -> None
-        assert name, "Type must be named."
-        assert_valid_name(name)
-        self.name = name
-        self.description = description
-
-        if resolve_type is not None:
-            assert callable(
-                resolve_type
-            ), '{} must provide "resolve_type" as a function.'.format(self)
-
-        self.resolve_type = resolve_type
-        self._types = types
-
-    @cached_property
-    def types(self):
-        # type: () -> List[GraphQLObjectType]
-        return define_types(self, self._types)
-
-
-# fmt: off
-def define_types(
-    union_type,  # type: GraphQLUnionType
-    types,  # type: Union[Callable[[], List[GraphQLObjectType]], List[GraphQLObjectType]]
-):
-    # type: (...) -> List[GraphQLObjectType]
-    # fmt: on
-    if callable(types):
-        types = types()
-
-    assert (
-        isinstance(types, (list, tuple)) and len(types) > 0
-    ), "Must provide types for Union {}.".format(union_type.name)
-    has_resolve_type_fn = callable(union_type.resolve_type)
-
-    for type in types:
-        assert isinstance(
-            type, GraphQLObjectType
-        ), "{} may only contain Object types, it cannot contain: {}.".format(
-            union_type, type
-        )
-
-        if not has_resolve_type_fn:
-            assert callable(type.is_type_of), (
-                'Union Type {} does not provide a "resolve_type" function '
-                'and possible Type {} does not provide a "is_type_of" '
-                "function. There is no way to resolve this possible type "
-                "during execution."
-            ).format(union_type, type)
-
-    return types
-
-
-class GraphQLEnumType(GraphQLNamedType):
-    """Enum Type Definition
-
-    Some leaf values of requests and input values are Enums. GraphQL serializes Enum values as strings,
-    however internally Enums can be represented by any kind of type, often integers.
-
-    Example:
-
-        RGBType = GraphQLEnumType(
-            name='RGB',
-            values=OrderedDict([
-                ('RED', GraphQLEnumValue(0)),
-                ('GREEN', GraphQLEnumValue(1)),
-                ('BLUE', GraphQLEnumValue(2))
-            ])
-        )
-
-    Note: If a value is not provided in a definition, the name of the enum value will be used as it's internal value.
-    """
-
-    def __init__(self, name, values, description=None):
-        assert name, "Type must provide name."
-        assert_valid_name(name)
-        self.name = name
-        self.description = description
-
-        self.values = define_enum_values(self, values)
-
-    def get_values(self):
-        return self.values
-
-    def get_value(self, name):
-        return self._name_lookup.get(name)
-
-    def serialize(self, value):
-        # type: (Union[str, PyEnum]) -> Optional[str]
-        if isinstance(value, PyEnum):
-            # We handle PyEnum values
-            value = value.value
-        if isinstance(value, Hashable):
-            enum_value = self._value_lookup.get(value)
-            if enum_value:
-                return enum_value.name
-
-        return None
-
-    def parse_value(self, value):
-        if isinstance(value, Hashable):
-            enum_value = self._name_lookup.get(value)
-
-            if enum_value:
-                return enum_value.value
-
-        return None
-
-    def parse_literal(self, value_ast):
-        if isinstance(value_ast, ast.EnumValue):
-            enum_value = self._name_lookup.get(value_ast.value)
-
-            if enum_value:
-                return enum_value.value
-
-    @cached_property
-    def _value_lookup(self):
-        # type: () -> Dict[str, GraphQLEnumValue]
-        return {value.value: value for value in self.values}
-
-    @cached_property
-    def _name_lookup(self):
-        return {value.name: value for value in self.values}
-
-
-def define_enum_values(type, value_map):
-    assert (
-        isinstance(value_map, Mapping) and len(value_map) > 0
-    ), "{} values must be a mapping (dict / OrderedDict) with value names as keys.".format(
-        type
-    )
-
-    values = []
-    if not isinstance(value_map, (collections.OrderedDict, OrderedDict)):
-        value_map = OrderedDict(sorted(list(value_map.items())))
-
-    for value_name, value in value_map.items():
-        assert_valid_name(value_name)
-        assert isinstance(
-            value, GraphQLEnumValue
-        ), "{}.{} must be an instance of GraphQLEnumValue, but got: {}".format(
-            type, value_name, value
-        )
-        value = copy.copy(value)
-        value.name = value_name
-        if value.value == Undefined:
-            value.value = value_name
-
-        values.append(value)
-
-    return values
-
-
-class GraphQLEnumValue(object):
-    __slots__ = "name", "value", "deprecation_reason", "description"
-
-    def __init__(
-        self, value=Undefined, deprecation_reason=None, description=None, name=None
-    ):
-        self.name = name
-        self.value = value
-        self.deprecation_reason = deprecation_reason
-        self.description = description
-
-    @property
-    def is_deprecated(self):
-        return bool(self.deprecation_reason)
-
-    def __eq__(self, other):
-        return self is other or (
-            isinstance(other, GraphQLEnumValue)
-            and self.name == other.name
-            and self.value == other.value
-            and self.deprecation_reason == other.deprecation_reason
-            and self.description == other.description
-        )
-
-
-class GraphQLInputObjectType(GraphQLNamedType):
-    """Input Object Type Definition
-
-    An input object defines a structured collection of fields which may be
-    supplied to a field argument.
-
-    Using `NonNull` will ensure that a value must be provided by the query
-
-    Example:
-
-        NonNullFloat = GraphQLNonNull(GraphQLFloat())
-
-        class GeoPoint(GraphQLInputObjectType):
-            name = 'GeoPoint'
-            fields = {
-                'lat': GraphQLInputObjectField(NonNullFloat),
-                'lon': GraphQLInputObjectField(NonNullFloat),
-                'alt': GraphQLInputObjectField(GraphQLFloat(),
-                    default_value=0)
-            }
-    """
-
-    def __init__(
-        self,
-        name,  # type: str
-        fields,  # type: Union[Callable[[], Dict[str, GraphQLInputObjectField]], Dict[str, GraphQLInputObjectField]]
-        description=None,  # type: Optional[str]
-        container_type=None,  # type: Type[Dict[str, Any]]
-    ):
-        # type: (...) -> None
-        assert name, "Type must be named."
-        self.name = name
-        self.description = description
-        if container_type is None:
-            container_type = dict  # type: ignore
-        assert callable(container_type), "container_type must be callable"
-        self.container_type = container_type
-        self._fields = fields
-
-    def create_container(self, data):
-        # type: (Dict[str, Any]) -> Dict[str, Any]
-        return self.container_type(data)
-
-    @cached_property
-    def fields(self):
-        # type: () -> Dict[str, GraphQLInputObjectField]
-        return self._define_field_map()
-
-    def _define_field_map(self):
-        # type: () -> OrderedDict
-        if callable(self._fields):
-            fields = self._fields()
-        else:
-            fields = self._fields
-
-        assert isinstance(fields, Mapping) and len(fields) > 0, (
-            "{} fields must be a mapping (dict / OrderedDict) with field names as keys or a "
-            "function which returns such a mapping."
-        ).format(self)
-        if not isinstance(fields, (collections.OrderedDict, OrderedDict)):
-            fields = OrderedDict(sorted(list(fields.items())))
-
-        for field_name, field in fields.items():
-            assert_valid_name(field_name)
-
-        return fields
-
-
-class GraphQLInputObjectField(object):
-    __slots__ = "type", "default_value", "description", "out_name"
-
-    def __init__(
-        self,
-        type,  # type: Union[GraphQLInputObjectType, GraphQLScalarType]
-        default_value=None,  # type: Optional[Any]
-        description=None,  # type: Optional[Any]
-        out_name=None,  # type: str
-    ):
-        # type: (...) -> None
-        self.type = type
-        self.default_value = default_value
-        self.description = description
-        self.out_name = out_name
-
-    def __eq__(self, other):
-        return self is other or (
-            isinstance(other, GraphQLInputObjectField)
-            and self.type == other.type
-            and self.description == other.description
-            and self.out_name == other.out_name
-        )
-
-
-class GraphQLList(GraphQLType):
-    """List Modifier
-
-    A list is a kind of type marker, a wrapping type which points to another
-    type. Lists are often created within the context of defining the fields
-    of an object type.
-
-    Example:
-
-        class PersonType(GraphQLObjectType):
-            name = 'Person'
-
-            def get_fields(self):
-                return {
-                    'parents': GraphQLField(GraphQLList(PersonType())),
-                    'children': GraphQLField(GraphQLList(PersonType())),
-                }
-    """
-
-    __slots__ = ("of_type",)
-
-    def __init__(self, type):
-        # type: (Any) -> None
-        assert is_type(
-            type
-        ), "Can only create List of a GraphQLType but got: {}.".format(type)
-        self.of_type = type
-
-    def __str__(self):
-        # type: () -> str
-        return "[" + str(self.of_type) + "]"
-
-    def is_same_type(self, other):
-        return isinstance(other, GraphQLList) and self.of_type.is_same_type(
-            other.of_type
-        )
-
-
-class GraphQLNonNull(GraphQLType):
-    """Non-Null Modifier
-
-    A non-null is a kind of type marker, a wrapping type which points to another type. Non-null types enforce that their values are never null
-    and can ensure an error is raised if this ever occurs during a request. It is useful for fields which you can make a strong guarantee on
-    non-nullability, for example usually the id field of a database row will never be null.
-
-    Example:
-
-        class RowType(GraphQLObjectType):
-            name = 'Row'
-            fields = {
-                'id': GraphQLField(type=GraphQLNonNull(GraphQLString()))
-            }
-
-    Note: the enforcement of non-nullability occurs within the executor.
-    """
-
-    __slots__ = ("of_type",)
-
-    def __init__(
-        self,
-        type,  # type: Union[GraphQLList, GraphQLObjectType, GraphQLScalarType, GraphQLInputObjectType, GraphQLInterfaceType]
-    ):
-        # type: (...) -> None
-        assert is_type(type) and not isinstance(
-            type, GraphQLNonNull
-        ), "Can only create NonNull of a Nullable GraphQLType but got: {}.".format(type)
-        self.of_type = type
-
-    def __str__(self):
-        # type: () -> str
-        return str(self.of_type) + "!"
-
-    def is_same_type(self, other):
-        return isinstance(other, GraphQLNonNull) and self.of_type.is_same_type(
-            other.of_type
-        )
diff --git a/graphql/type/directives.py b/graphql/type/directives.py
deleted file mode 100644
index 9e398a1..0000000
--- a/graphql/type/directives.py
+++ /dev/null
@@ -1,125 +0,0 @@
-try:
-    from collections.abc import Iterable, Mapping
-except ImportError:  # Python < 3.3
-    from collections import Iterable, Mapping
-
-from ..pyutils.ordereddict import OrderedDict
-from ..utils.assert_valid_name import assert_valid_name
-from .definition import GraphQLArgument, GraphQLNonNull, is_input_type
-from .scalars import GraphQLBoolean, GraphQLString
-
-
-class DirectiveLocation(object):
-    # Operations
-    QUERY = "QUERY"
-    MUTATION = "MUTATION"
-    SUBSCRIPTION = "SUBSCRIPTION"
-    FIELD = "FIELD"
-    FRAGMENT_DEFINITION = "FRAGMENT_DEFINITION"
-    FRAGMENT_SPREAD = "FRAGMENT_SPREAD"
-    INLINE_FRAGMENT = "INLINE_FRAGMENT"
-
-    # Schema Definitions
-    SCHEMA = "SCHEMA"
-    SCALAR = "SCALAR"
-    OBJECT = "OBJECT"
-    FIELD_DEFINITION = "FIELD_DEFINITION"
-    ARGUMENT_DEFINITION = "ARGUMENT_DEFINITION"
-    INTERFACE = "INTERFACE"
-    UNION = "UNION"
-    ENUM = "ENUM"
-    ENUM_VALUE = "ENUM_VALUE"
-    INPUT_OBJECT = "INPUT_OBJECT"
-    INPUT_FIELD_DEFINITION = "INPUT_FIELD_DEFINITION"
-
-    OPERATION_LOCATIONS = [QUERY, MUTATION, SUBSCRIPTION]
-
-    FRAGMENT_LOCATIONS = [FRAGMENT_DEFINITION, FRAGMENT_SPREAD, INLINE_FRAGMENT]
-
-    FIELD_LOCATIONS = [FIELD]
-
-
-class GraphQLDirective(object):
-    __slots__ = "name", "args", "description", "locations"
-
-    def __init__(self, name, description=None, args=None, locations=None):
-        assert name, "Directive must be named."
-        assert_valid_name(name)
-        assert isinstance(locations, Iterable), "Must provide locations for directive."
-
-        self.name = name
-        self.description = description
-        self.locations = locations
-
-        if args:
-            assert isinstance(
-                args, Mapping
-            ), "{} args must be a dict with argument names as keys.".format(name)
-            for arg_name, _arg in args.items():
-                assert_valid_name(arg_name)
-                assert is_input_type(
-                    _arg.type
-                ), "{}({}) argument type must be Input Type but got {}.".format(
-                    name, arg_name, _arg.type
-                )
-        self.args = args or OrderedDict()
-
-
-"""Used to conditionally include fields or fragments."""
-GraphQLIncludeDirective = GraphQLDirective(
-    name="include",
-    description="Directs the executor to include this field or fragment only when the `if` argument is true.",
-    args={
-        "if": GraphQLArgument(
-            type=GraphQLNonNull(GraphQLBoolean), description="Included when true."
-        )
-    },
-    locations=[
-        DirectiveLocation.FIELD,
-        DirectiveLocation.FRAGMENT_SPREAD,
-        DirectiveLocation.INLINE_FRAGMENT,
-    ],
-)
-
-"""Used to conditionally skip (exclude) fields or fragments."""
-GraphQLSkipDirective = GraphQLDirective(
-    name="skip",
-    description="Directs the executor to skip this field or fragment when the `if` argument is true.",
-    args={
-        "if": GraphQLArgument(
-            type=GraphQLNonNull(GraphQLBoolean), description="Skipped when true."
-        )
-    },
-    locations=[
-        DirectiveLocation.FIELD,
-        DirectiveLocation.FRAGMENT_SPREAD,
-        DirectiveLocation.INLINE_FRAGMENT,
-    ],
-)
-
-"""Constant string used for default reason for a deprecation."""
-DEFAULT_DEPRECATION_REASON = "No longer supported"
-
-"""Used to declare element of a GraphQL schema as deprecated."""
-GraphQLDeprecatedDirective = GraphQLDirective(
-    name="deprecated",
-    description="Marks an element of a GraphQL schema as no longer supported.",
-    args={
-        "reason": GraphQLArgument(
-            type=GraphQLString,
-            description=(
-                "Explains why this element was deprecated, usually also including a suggestion for how to"
-                "access supported similar data. Formatted in [Markdown]"
-                "(https://daringfireball.net/projects/markdown/)."
-            ),
-            default_value=DEFAULT_DEPRECATION_REASON,
-        )
-    },
-    locations=[DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE],
-)
-
-specified_directives = [
-    GraphQLIncludeDirective,
-    GraphQLSkipDirective,
-    GraphQLDeprecatedDirective,
-]
diff --git a/graphql/type/introspection.py b/graphql/type/introspection.py
deleted file mode 100644
index 520011f..0000000
--- a/graphql/type/introspection.py
+++ /dev/null
@@ -1,667 +0,0 @@
-from collections import OrderedDict, namedtuple
-
-from ..language.printer import print_ast
-from ..utils.ast_from_value import ast_from_value
-from .definition import (
-    GraphQLArgument,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLInputObjectType,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLUnionType,
-)
-from .directives import DirectiveLocation
-from .scalars import GraphQLBoolean, GraphQLString
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..execution.base import ResolveInfo
-    from .definition import GraphQLInputObjectField
-    from typing import Union, List, Optional, Any, Dict
-
-InputField = namedtuple("InputField", ["name", "description", "type", "default_value"])
-Field = namedtuple(
-    "Field", ["name", "type", "description", "args", "deprecation_reason"]
-)
-
-
-def input_fields_to_list(input_fields):
-    # type: (Dict[str, GraphQLInputObjectField]) -> List[InputField]
-    fields = []
-    for field_name, field in input_fields.items():
-        fields.append(
-            InputField(
-                name=field_name,
-                description=field.description,
-                type=field.type,
-                default_value=field.default_value,
-            )
-        )
-    return fields
-
-
-__Schema = GraphQLObjectType(
-    "__Schema",
-    description="A GraphQL Schema defines the capabilities of a GraphQL server. It "
-    "exposes all available types and directives on the server, as well as "
-    "the entry points for query, mutation and subscription operations.",
-    fields=lambda: OrderedDict(
-        [
-            (
-                "types",
-                GraphQLField(
-                    description="A list of all types supported by this server.",
-                    type=GraphQLNonNull(
-                        GraphQLList(GraphQLNonNull(__Type))  # type: ignore
-                    ),
-                    resolver=lambda schema, *_: schema.get_type_map().values(),
-                ),
-            ),
-            (
-                "queryType",
-                GraphQLField(
-                    description="The type that query operations will be rooted at.",
-                    type=GraphQLNonNull(__Type),  # type: ignore
-                    resolver=lambda schema, *_: schema.get_query_type(),
-                ),
-            ),
-            (
-                "mutationType",
-                GraphQLField(
-                    description="If this server supports mutation, the type that "
-                    "mutation operations will be rooted at.",
-                    type=__Type,  # type: ignore
-                    resolver=lambda schema, *_: schema.get_mutation_type(),
-                ),
-            ),
-            (
-                "subscriptionType",
-                GraphQLField(
-                    description="If this server support subscription, the type "
-                    "that subscription operations will be rooted at.",
-                    type=__Type,  # type: ignore
-                    resolver=lambda schema, *_: schema.get_subscription_type(),
-                ),
-            ),
-            (
-                "directives",
-                GraphQLField(
-                    description="A list of all directives supported by this server.",
-                    type=GraphQLNonNull(
-                        GraphQLList(GraphQLNonNull(__Directive))  # type: ignore
-                    ),
-                    resolver=lambda schema, *_: schema.get_directives(),
-                ),
-            ),
-        ]
-    ),
-)
-
-_on_operation_locations = set(DirectiveLocation.OPERATION_LOCATIONS)
-_on_fragment_locations = set(DirectiveLocation.FRAGMENT_LOCATIONS)
-_on_field_locations = set(DirectiveLocation.FIELD_LOCATIONS)
-
-__Directive = GraphQLObjectType(
-    "__Directive",
-    description="A Directive provides a way to describe alternate runtime execution and "
-    "type validation behavior in a GraphQL document."
-    "\n\nIn some cases, you need to provide options to alter GraphQL's "
-    "execution behavior in ways field arguments will not suffice, such as "
-    "conditionally including or skipping a field. Directives provide this by "
-    "describing additional information to the executor.",
-    fields=lambda: OrderedDict(
-        [
-            ("name", GraphQLField(GraphQLNonNull(GraphQLString))),
-            ("description", GraphQLField(GraphQLString)),
-            (
-                "locations",
-                GraphQLField(
-                    type=GraphQLNonNull(
-                        GraphQLList(GraphQLNonNull(__DirectiveLocation))  # type: ignore
-                    )
-                ),
-            ),
-            (
-                "args",
-                GraphQLField(
-                    type=GraphQLNonNull(
-                        GraphQLList(GraphQLNonNull(__InputValue))  # type: ignore
-                    ),
-                    resolver=lambda directive, *args: input_fields_to_list(
-                        directive.args
-                    ),
-                ),
-            ),
-            (
-                "onOperation",
-                GraphQLField(
-                    type=GraphQLNonNull(GraphQLBoolean),
-                    deprecation_reason="Use `locations`.",
-                    resolver=lambda directive, *args: set(directive.locations)
-                    & _on_operation_locations,
-                ),
-            ),
-            (
-                "onFragment",
-                GraphQLField(
-                    type=GraphQLNonNull(GraphQLBoolean),
-                    deprecation_reason="Use `locations`.",
-                    resolver=lambda directive, *args: set(directive.locations)
-                    & _on_fragment_locations,
-                ),
-            ),
-            (
-                "onField",
-                GraphQLField(
-                    type=GraphQLNonNull(GraphQLBoolean),
-                    deprecation_reason="Use `locations`.",
-                    resolver=lambda directive, *args: set(directive.locations)
-                    & _on_field_locations,
-                ),
-            ),
-        ]
-    ),
-)
-
-__DirectiveLocation = GraphQLEnumType(
-    "__DirectiveLocation",
-    description=(
-        "A Directive can be adjacent to many parts of the GraphQL language, a "
-        + "__DirectiveLocation describes one such possible adjacencies."
-    ),
-    values=OrderedDict(
-        [
-            (
-                "QUERY",
-                GraphQLEnumValue(
-                    DirectiveLocation.QUERY,
-                    description="Location adjacent to a query operation.",
-                ),
-            ),
-            (
-                "MUTATION",
-                GraphQLEnumValue(
-                    DirectiveLocation.MUTATION,
-                    description="Location adjacent to a mutation operation.",
-                ),
-            ),
-            (
-                "SUBSCRIPTION",
-                GraphQLEnumValue(
-                    DirectiveLocation.SUBSCRIPTION,
-                    description="Location adjacent to a subscription operation.",
-                ),
-            ),
-            (
-                "FIELD",
-                GraphQLEnumValue(
-                    DirectiveLocation.FIELD, description="Location adjacent to a field."
-                ),
-            ),
-            (
-                "FRAGMENT_DEFINITION",
-                GraphQLEnumValue(
-                    DirectiveLocation.FRAGMENT_DEFINITION,
-                    description="Location adjacent to a fragment definition.",
-                ),
-            ),
-            (
-                "FRAGMENT_SPREAD",
-                GraphQLEnumValue(
-                    DirectiveLocation.FRAGMENT_SPREAD,
-                    description="Location adjacent to a fragment spread.",
-                ),
-            ),
-            (
-                "INLINE_FRAGMENT",
-                GraphQLEnumValue(
-                    DirectiveLocation.INLINE_FRAGMENT,
-                    description="Location adjacent to an inline fragment.",
-                ),
-            ),
-            (
-                "SCHEMA",
-                GraphQLEnumValue(
-                    DirectiveLocation.SCHEMA,
-                    description="Location adjacent to a schema definition.",
-                ),
-            ),
-            (
-                "SCALAR",
-                GraphQLEnumValue(
-                    DirectiveLocation.SCALAR,
-                    description="Location adjacent to a scalar definition.",
-                ),
-            ),
-            (
-                "OBJECT",
-                GraphQLEnumValue(
-                    DirectiveLocation.OBJECT,
-                    description="Location adjacent to an object definition.",
-                ),
-            ),
-            (
-                "FIELD_DEFINITION",
-                GraphQLEnumValue(
-                    DirectiveLocation.FIELD_DEFINITION,
-                    description="Location adjacent to a field definition.",
-                ),
-            ),
-            (
-                "ARGUMENT_DEFINITION",
-                GraphQLEnumValue(
-                    DirectiveLocation.ARGUMENT_DEFINITION,
-                    description="Location adjacent to an argument definition.",
-                ),
-            ),
-            (
-                "INTERFACE",
-                GraphQLEnumValue(
-                    DirectiveLocation.INTERFACE,
-                    description="Location adjacent to an interface definition.",
-                ),
-            ),
-            (
-                "UNION",
-                GraphQLEnumValue(
-                    DirectiveLocation.UNION,
-                    description="Location adjacent to a union definition.",
-                ),
-            ),
-            (
-                "ENUM",
-                GraphQLEnumValue(
-                    DirectiveLocation.ENUM,
-                    description="Location adjacent to an enum definition.",
-                ),
-            ),
-            (
-                "ENUM_VALUE",
-                GraphQLEnumValue(
-                    DirectiveLocation.ENUM_VALUE,
-                    description="Location adjacent to an enum value definition.",
-                ),
-            ),
-            (
-                "INPUT_OBJECT",
-                GraphQLEnumValue(
-                    DirectiveLocation.INPUT_OBJECT,
-                    description="Location adjacent to an input object definition.",
-                ),
-            ),
-            (
-                "INPUT_FIELD_DEFINITION",
-                GraphQLEnumValue(
-                    DirectiveLocation.INPUT_FIELD_DEFINITION,
-                    description="Location adjacent to an input object field definition.",
-                ),
-            ),
-        ]
-    ),
-)
-
-
-class TypeKind(object):
-    SCALAR = "SCALAR"
-    OBJECT = "OBJECT"
-    INTERFACE = "INTERFACE"
-    UNION = "UNION"
-    ENUM = "ENUM"
-    INPUT_OBJECT = "INPUT_OBJECT"
-    LIST = "LIST"
-    NON_NULL = "NON_NULL"
-
-
-class TypeFieldResolvers(object):
-    _kinds = (
-        (GraphQLScalarType, TypeKind.SCALAR),
-        (GraphQLObjectType, TypeKind.OBJECT),
-        (GraphQLInterfaceType, TypeKind.INTERFACE),
-        (GraphQLUnionType, TypeKind.UNION),
-        (GraphQLEnumType, TypeKind.ENUM),
-        (GraphQLInputObjectType, TypeKind.INPUT_OBJECT),
-        (GraphQLList, TypeKind.LIST),
-        (GraphQLNonNull, TypeKind.NON_NULL),
-    )
-
-    @classmethod
-    def kind(
-        cls,
-        type,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-        *_  # type: ResolveInfo
-    ):
-        # type: (...) -> str
-        for klass, kind in cls._kinds:
-            if isinstance(type, klass):
-                return kind
-
-        raise Exception("Unknown kind of type: {}".format(type))
-
-    @staticmethod
-    def fields(
-        type,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-        info,  # type: ResolveInfo
-        includeDeprecated=None,  # type: bool
-    ):
-        # type: (...) -> Optional[List[Field]]
-        if isinstance(type, (GraphQLObjectType, GraphQLInterfaceType)):
-            fields = []
-            include_deprecated = includeDeprecated
-            for field_name, field in type.fields.items():
-                if field.deprecation_reason and not include_deprecated:
-                    continue
-                fields.append(
-                    Field(
-                        name=field_name,
-                        description=field.description,
-                        type=field.type,
-                        args=field.args,
-                        deprecation_reason=field.deprecation_reason,
-                    )
-                )
-            return fields
-        return None
-
-    @staticmethod
-    def interfaces(type, info):
-        # type: (Optional[GraphQLObjectType], ResolveInfo) -> Optional[List[GraphQLInterfaceType]]
-        if isinstance(type, GraphQLObjectType):
-            return type.interfaces
-        return None
-
-    @staticmethod
-    def possible_types(
-        type,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-        info,  # type: ResolveInfo
-        **args  # type: Any
-    ):
-        # type: (...) -> List[GraphQLObjectType]
-        if isinstance(type, (GraphQLInterfaceType, GraphQLUnionType)):
-            return info.schema.get_possible_types(type)
-
-    @staticmethod
-    def enum_values(
-        type,  # type: GraphQLEnumType
-        info,  # type: ResolveInfo
-        includeDeprecated=None,  # type: bool
-    ):
-        # type: (...) -> Optional[List[GraphQLEnumValue]]
-        if isinstance(type, GraphQLEnumType):
-            values = type.values
-            if not includeDeprecated:
-                values = [v for v in values if not v.deprecation_reason]
-
-            return values
-        return None
-
-    @staticmethod
-    def input_fields(type, info):
-        # type: (GraphQLInputObjectType, ResolveInfo) -> List[InputField]
-        if isinstance(type, GraphQLInputObjectType):
-            return input_fields_to_list(type.fields)
-
-
-__Type = GraphQLObjectType(
-    "__Type",
-    description="The fundamental unit of any GraphQL Schema is the type. There are "
-    "many kinds of types in GraphQL as represented by the `__TypeKind` enum."
-    "\n\nDepending on the kind of a type, certain fields describe "
-    "information about that type. Scalar types provide no information "
-    "beyond a name and description, while Enum types provide their values. "
-    "Object and Interface types provide the fields they describe. Abstract "
-    "types, Union and Interface, provide the Object types possible "
-    "at runtime. List and NonNull types compose other types.",
-    fields=lambda: OrderedDict(
-        [
-            (
-                "kind",
-                GraphQLField(
-                    type=GraphQLNonNull(__TypeKind),  # type: ignore
-                    resolver=TypeFieldResolvers.kind,
-                ),
-            ),
-            ("name", GraphQLField(GraphQLString)),
-            ("description", GraphQLField(GraphQLString)),
-            (
-                "fields",
-                GraphQLField(
-                    type=GraphQLList(GraphQLNonNull(__Field)),  # type: ignore
-                    args={
-                        "includeDeprecated": GraphQLArgument(
-                            GraphQLBoolean, default_value=False
-                        )
-                    },
-                    resolver=TypeFieldResolvers.fields,
-                ),
-            ),
-            (
-                "interfaces",
-                GraphQLField(
-                    type=GraphQLList(GraphQLNonNull(__Type)),  # type: ignore
-                    resolver=TypeFieldResolvers.interfaces,
-                ),
-            ),
-            (
-                "possibleTypes",
-                GraphQLField(
-                    type=GraphQLList(GraphQLNonNull(__Type)),  # type: ignore
-                    resolver=TypeFieldResolvers.possible_types,
-                ),
-            ),
-            (
-                "enumValues",
-                GraphQLField(
-                    type=GraphQLList(GraphQLNonNull(__EnumValue)),  # type: ignore
-                    args={
-                        "includeDeprecated": GraphQLArgument(
-                            GraphQLBoolean, default_value=False
-                        )
-                    },
-                    resolver=TypeFieldResolvers.enum_values,
-                ),
-            ),
-            (
-                "inputFields",
-                GraphQLField(
-                    type=GraphQLList(GraphQLNonNull(__InputValue)),  # type: ignore
-                    resolver=TypeFieldResolvers.input_fields,
-                ),
-            ),
-            (
-                "ofType",
-                GraphQLField(
-                    type=__Type,  # type: ignore
-                    resolver=lambda type, *_: getattr(type, "of_type", None),
-                ),
-            ),
-        ]
-    ),
-)
-
-__Field = GraphQLObjectType(
-    "__Field",
-    description="Object and Interface types are described by a list of Fields, each of "
-    "which has a name, potentially a list of arguments, and a return type.",
-    fields=lambda: OrderedDict(
-        [
-            ("name", GraphQLField(GraphQLNonNull(GraphQLString))),
-            ("description", GraphQLField(GraphQLString)),
-            (
-                "args",
-                GraphQLField(
-                    type=GraphQLNonNull(
-                        GraphQLList(GraphQLNonNull(__InputValue))  # type: ignore
-                    ),
-                    resolver=lambda field, *_: input_fields_to_list(field.args),
-                ),
-            ),
-            ("type", GraphQLField(GraphQLNonNull(__Type))),  # type: ignore
-            (
-                "isDeprecated",
-                GraphQLField(
-                    type=GraphQLNonNull(GraphQLBoolean),
-                    resolver=lambda field, *_: bool(field.deprecation_reason),
-                ),
-            ),
-            (
-                "deprecationReason",
-                GraphQLField(
-                    type=GraphQLString,
-                    resolver=lambda field, *_: field.deprecation_reason,
-                ),
-            ),
-        ]
-    ),
-)
-
-__InputValue = GraphQLObjectType(
-    "__InputValue",
-    description="Arguments provided to Fields or Directives and the input fields of an "
-    "InputObject are represented as Input Values which describe their type "
-    "and optionally a default value.",
-    fields=lambda: OrderedDict(
-        [
-            ("name", GraphQLField(GraphQLNonNull(GraphQLString))),
-            ("description", GraphQLField(GraphQLString)),
-            ("type", GraphQLField(GraphQLNonNull(__Type))),
-            (
-                "defaultValue",
-                GraphQLField(
-                    type=GraphQLString,
-                    resolver=lambda input_val, *_: None
-                    if input_val.default_value is None
-                    else print_ast(ast_from_value(input_val.default_value, input_val)),
-                ),
-            ),
-        ]
-    ),
-)
-
-__EnumValue = GraphQLObjectType(
-    "__EnumValue",
-    description="One possible value for a given Enum. Enum values are unique values, not "
-    "a placeholder for a string or numeric value. However an Enum value is "
-    "returned in a JSON response as a string.",
-    fields=lambda: OrderedDict(
-        [
-            ("name", GraphQLField(GraphQLNonNull(GraphQLString))),
-            ("description", GraphQLField(GraphQLString)),
-            (
-                "isDeprecated",
-                GraphQLField(
-                    type=GraphQLNonNull(GraphQLBoolean),
-                    resolver=lambda field, *_: bool(field.deprecation_reason),
-                ),
-            ),
-            (
-                "deprecationReason",
-                GraphQLField(
-                    type=GraphQLString,
-                    resolver=lambda enum_value, *_: enum_value.deprecation_reason,
-                ),
-            ),
-        ]
-    ),
-)
-
-__TypeKind = GraphQLEnumType(
-    "__TypeKind",
-    description="An enum describing what kind of type a given `__Type` is",
-    values=OrderedDict(
-        [
-            (
-                "SCALAR",
-                GraphQLEnumValue(
-                    TypeKind.SCALAR, description="Indicates this type is a scalar."
-                ),
-            ),
-            (
-                "OBJECT",
-                GraphQLEnumValue(
-                    TypeKind.OBJECT,
-                    description="Indicates this type is an object. "
-                    "`fields` and `interfaces` are valid fields.",
-                ),
-            ),
-            (
-                "INTERFACE",
-                GraphQLEnumValue(
-                    TypeKind.INTERFACE,
-                    description="Indicates this type is an interface. "
-                    "`fields` and `possibleTypes` are valid fields.",
-                ),
-            ),
-            (
-                "UNION",
-                GraphQLEnumValue(
-                    TypeKind.UNION,
-                    description="Indicates this type is a union. "
-                    "`possibleTypes` is a valid field.",
-                ),
-            ),
-            (
-                "ENUM",
-                GraphQLEnumValue(
-                    TypeKind.ENUM,
-                    description="Indicates this type is an enum. "
-                    "`enumValues` is a valid field.",
-                ),
-            ),
-            (
-                "INPUT_OBJECT",
-                GraphQLEnumValue(
-                    TypeKind.INPUT_OBJECT,
-                    description="Indicates this type is an input object. "
-                    "`inputFields` is a valid field.",
-                ),
-            ),
-            (
-                "LIST",
-                GraphQLEnumValue(
-                    TypeKind.LIST,
-                    description="Indicates this type is a list. "
-                    "`ofType` is a valid field.",
-                ),
-            ),
-            (
-                "NON_NULL",
-                GraphQLEnumValue(
-                    TypeKind.NON_NULL,
-                    description="Indicates this type is a non-null. "
-                    "`ofType` is a valid field.",
-                ),
-            ),
-        ]
-    ),
-)
-
-IntrospectionSchema = __Schema
-
-SchemaMetaFieldDef = GraphQLField(
-    # name='__schema',
-    type=GraphQLNonNull(__Schema),
-    description="Access the current type schema of this server.",
-    resolver=lambda source, info, **args: info.schema,
-    args={},
-)
-
-TypeMetaFieldDef = GraphQLField(
-    type=__Type,
-    # name='__type',
-    description="Request the type information of a single type.",
-    args={"name": GraphQLArgument(GraphQLNonNull(GraphQLString))},
-    resolver=lambda source, info, **args: info.schema.get_type(args["name"]),
-)
-
-TypeNameMetaFieldDef = GraphQLField(
-    type=GraphQLNonNull(GraphQLString),
-    # name='__typename',
-    description="The name of the current Object type at runtime.",
-    resolver=lambda source, info, **args: info.parent_type.name,
-    args={},
-)
diff --git a/graphql/type/scalars.py b/graphql/type/scalars.py
deleted file mode 100644
index 137cf27..0000000
--- a/graphql/type/scalars.py
+++ /dev/null
@@ -1,153 +0,0 @@
-from six import string_types, text_type
-
-from ..language.ast import BooleanValue, FloatValue, IntValue, StringValue
-from .definition import GraphQLScalarType
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, Optional, Union
-
-# As per the GraphQL Spec, Integers are only treated as valid when a valid
-# 32-bit signed integer, providing the broadest support across platforms.
-#
-# n.b. JavaScript's integers are safe between -(2^31 - 1) and 2^31 - 1 because
-# they are internally represented as IEEE 754 doubles.
-MAX_INT = 2147483647
-MIN_INT = -2147483648
-
-
-def coerce_int(value):
-    # type: (Any) -> int
-    if isinstance(value, int):
-        num = value
-    else:
-        try:
-            num = int(value)
-        except ValueError:
-            num = int(float(value))
-    if MIN_INT <= num <= MAX_INT:
-        return num
-
-    raise Exception(
-        ("Int cannot represent non 32-bit signed integer value: {}").format(value)
-    )
-
-
-def parse_int_literal(ast):
-    # type: (IntValue) -> Optional[int]
-    if isinstance(ast, IntValue):
-        num = int(ast.value)
-        if MIN_INT <= num <= MAX_INT:
-            return num
-    return None
-
-
-GraphQLInt = GraphQLScalarType(
-    name="Int",
-    description="The `Int` scalar type represents non-fractional signed whole numeric "
-    "values. Int can represent values between -(2^31 - 1) and 2^31 - 1 since "
-    "represented in JSON as double-precision floating point numbers specified"
-    "by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).",
-    serialize=coerce_int,
-    parse_value=coerce_int,
-    parse_literal=parse_int_literal,
-)
-
-
-def coerce_float(value):
-    # type: (Any) -> float
-    if isinstance(value, float):
-        return value
-    return float(value)
-
-
-def parse_float_literal(ast):
-    # type: (Union[FloatValue, IntValue]) -> Optional[float]
-    if isinstance(ast, (FloatValue, IntValue)):
-        return float(ast.value)
-    return None
-
-
-GraphQLFloat = GraphQLScalarType(
-    name="Float",
-    description="The `Float` scalar type represents signed double-precision fractional "
-    "values as specified by "
-    "[IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). ",
-    serialize=coerce_float,
-    parse_value=coerce_float,
-    parse_literal=parse_float_literal,
-)
-
-
-def coerce_string(value):
-    # type: (Any) -> str
-    if isinstance(value, string_types):
-        return value
-
-    if isinstance(value, bool):
-        return u"true" if value else u"false"
-
-    return text_type(value)
-
-
-def coerce_str(value):
-    # type: (Any) -> str
-    if isinstance(value, string_types):
-        return value
-
-    return text_type(value)
-
-
-def parse_string_literal(ast):
-    # type: (Union[StringValue]) -> Optional[str]
-    if isinstance(ast, StringValue):
-        return ast.value
-
-    return None
-
-
-GraphQLString = GraphQLScalarType(
-    name="String",
-    description="The `String` scalar type represents textual data, represented as UTF-8 "
-    "character sequences. The String type is most often used by GraphQL to "
-    "represent free-form human-readable text.",
-    serialize=coerce_string,
-    parse_value=coerce_string,
-    parse_literal=parse_string_literal,
-)
-
-
-def parse_boolean_literal(ast):
-    # type: (BooleanValue) -> Optional[bool]
-    if isinstance(ast, BooleanValue):
-        return ast.value
-    return None
-
-
-GraphQLBoolean = GraphQLScalarType(
-    name="Boolean",
-    description="The `Boolean` scalar type represents `true` or `false`.",
-    serialize=bool,
-    parse_value=bool,
-    parse_literal=parse_boolean_literal,
-)
-
-
-def parse_id_literal(ast):
-    # type: (StringValue) -> Optional[str]
-    if isinstance(ast, (StringValue, IntValue)):
-        return ast.value
-    return None
-
-
-GraphQLID = GraphQLScalarType(
-    name="ID",
-    description="The `ID` scalar type represents a unique identifier, often used to "
-    "refetch an object or as key for a cache. The ID type appears in a JSON "
-    "response as a String; however, it is not intended to be human-readable. "
-    'When expected as an input type, any string (such as `"4"`) or integer '
-    "(such as `4`) input value will be accepted as an ID.",
-    serialize=coerce_str,
-    parse_value=coerce_str,
-    parse_literal=parse_id_literal,
-)
diff --git a/graphql/type/schema.py b/graphql/type/schema.py
deleted file mode 100644
index 697a8ae..0000000
--- a/graphql/type/schema.py
+++ /dev/null
@@ -1,149 +0,0 @@
-try:
-    from collections.abc import Iterable
-except ImportError:  # Python < 3.3
-    from collections import Iterable
-
-from .definition import GraphQLObjectType
-from .directives import GraphQLDirective, specified_directives
-from .introspection import IntrospectionSchema
-from .typemap import GraphQLTypeMap
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from .definition import (
-        GraphQLNamedType,
-        GraphQLInterfaceType,
-        GraphQLUnionType,
-        GraphQLType,
-    )
-    from typing import Dict, Union, List, Optional
-
-
-class GraphQLSchema(object):
-    """Schema Definition
-
-    A Schema is created by supplying the root types of each type of operation, query and mutation (optional).
-    A schema definition is then supplied to the validator and executor.
-
-    Example:
-
-        MyAppSchema = GraphQLSchema(
-            query=MyAppQueryRootType,
-            mutation=MyAppMutationRootType,
-        )
-
-    Note: If an array of `directives` are provided to GraphQLSchema, that will be
-    the exact list of directives represented and allowed. If `directives` is not
-    provided then a default set of the specified directives (e.g. @include and
-    @skip) will be used. If you wish to provide *additional* directives to these
-    specified directives, you must explicitly declare them. Example:
-
-      MyAppSchema = GraphQLSchema(
-          ...
-          directives=specified_directives.extend([MyCustomerDirective]),
-      )
-    """
-
-    __slots__ = (
-        "_query",
-        "_mutation",
-        "_subscription",
-        "_type_map",
-        "_directives",
-        "_implementations",
-        "_possible_type_map",
-    )
-
-    def __init__(
-        self,
-        query,  # type: GraphQLObjectType
-        mutation=None,  # type: Optional[GraphQLObjectType]
-        subscription=None,  # type: Optional[GraphQLObjectType]
-        directives=None,  # type: Optional[List[GraphQLDirective]]
-        types=None,  # type: Optional[List[GraphQLNamedType]]
-    ):
-        # type: (...) -> None
-        assert isinstance(
-            query, GraphQLObjectType
-        ), "Schema query must be Object Type but got: {}.".format(query)
-        if mutation:
-            assert isinstance(
-                mutation, GraphQLObjectType
-            ), "Schema mutation must be Object Type but got: {}.".format(mutation)
-
-        if subscription:
-            assert isinstance(
-                subscription, GraphQLObjectType
-            ), "Schema subscription must be Object Type but got: {}.".format(
-                subscription
-            )
-
-        if types:
-            assert isinstance(
-                types, Iterable
-            ), "Schema types must be iterable if provided but got: {}.".format(types)
-
-        self._query = query
-        self._mutation = mutation
-        self._subscription = subscription
-        if directives is None:
-            directives = specified_directives
-
-        assert all(
-            isinstance(d, GraphQLDirective) for d in directives
-        ), "Schema directives must be List[GraphQLDirective] if provided but got: {}.".format(
-            directives
-        )
-        self._directives = directives
-
-        initial_types = list(
-            filter(None, [query, mutation, subscription, IntrospectionSchema])
-        )  # type: List[GraphQLNamedType]
-        if types:
-            initial_types += types
-        self._type_map = GraphQLTypeMap(initial_types)  # type: GraphQLTypeMap
-
-    def get_query_type(self):
-        # type: () -> GraphQLObjectType
-        return self._query
-
-    def get_mutation_type(self):
-        # type: () -> Optional[GraphQLObjectType]
-        return self._mutation
-
-    def get_subscription_type(self):
-        # type: () -> Optional[GraphQLObjectType]
-        return self._subscription
-
-    def get_type_map(self):
-        # type: () -> Dict[str, GraphQLType]
-        return self._type_map
-
-    def get_type(self, name):
-        # type: (str) -> Optional[GraphQLNamedType]
-        return self._type_map.get(name)
-        # raise Exception("Type {name} not found in schema.".format(name=name))
-
-    def get_directives(self):
-        # type: () -> List[GraphQLDirective]
-        return self._directives
-
-    def get_directive(self, name):
-        # type: (str) -> Optional[GraphQLDirective]
-        for directive in self.get_directives():
-            if directive.name == name:
-                return directive
-
-        return None
-
-    def get_possible_types(self, abstract_type):
-        # type: (Union[GraphQLInterfaceType, GraphQLUnionType]) -> List[GraphQLObjectType]
-        return self._type_map.get_possible_types(abstract_type)
-
-    def is_possible_type(
-        self,
-        abstract_type,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-        possible_type,  # type: GraphQLObjectType
-    ):
-        # type: (...) -> bool
-        return self._type_map.is_possible_type(abstract_type, possible_type)
diff --git a/graphql/type/tests/__init__.py b/graphql/type/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/type/tests/test_definition.py b/graphql/type/tests/test_definition.py
deleted file mode 100644
index 944694e..0000000
--- a/graphql/type/tests/test_definition.py
+++ /dev/null
@@ -1,425 +0,0 @@
-from collections import OrderedDict
-from py.test import raises
-
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLBoolean,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInt,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-)
-from graphql.type.definition import is_input_type, is_output_type
-
-BlogImage = GraphQLObjectType(
-    "Image",
-    {
-        "url": GraphQLField(GraphQLString),
-        "width": GraphQLField(GraphQLInt),
-        "height": GraphQLField(GraphQLInt),
-    },
-)
-
-BlogAuthor = GraphQLObjectType(
-    "Author",
-    lambda: {
-        "id": GraphQLField(GraphQLString),
-        "name": GraphQLField(GraphQLString),
-        "pic": GraphQLField(
-            BlogImage,
-            args={
-                "width": GraphQLArgument(GraphQLInt),
-                "height": GraphQLArgument(GraphQLInt),
-            },
-        ),
-        "recentArticle": GraphQLField(BlogArticle),
-    },
-)
-
-BlogArticle = GraphQLObjectType(
-    "Article",
-    lambda: {
-        "id": GraphQLField(GraphQLString),
-        "isPublished": GraphQLField(GraphQLBoolean),
-        "author": GraphQLField(BlogAuthor),
-        "title": GraphQLField(GraphQLString),
-        "body": GraphQLField(GraphQLString),
-    },
-)
-
-BlogQuery = GraphQLObjectType(
-    "Query",
-    {
-        "article": GraphQLField(
-            BlogArticle, args={"id": GraphQLArgument(GraphQLString)}
-        ),
-        "feed": GraphQLField(GraphQLList(BlogArticle)),
-    },
-)
-
-BlogMutation = GraphQLObjectType(
-    "Mutation", {"writeArticle": GraphQLField(BlogArticle)}
-)
-
-BlogSubscription = GraphQLObjectType(
-    "Subscription",
-    {
-        "articleSubscribe": GraphQLField(
-            args={"id": GraphQLArgument(GraphQLString)}, type=BlogArticle
-        )
-    },
-)
-
-ObjectType = GraphQLObjectType("Object", {})
-InterfaceType = GraphQLInterfaceType("Interface")
-UnionType = GraphQLUnionType("Union", [ObjectType], resolve_type=lambda: None)
-EnumType = GraphQLEnumType("Enum", {"foo": GraphQLEnumValue()})
-InputObjectType = GraphQLInputObjectType("InputObject", {})
-
-
-def test_defines_a_query_only_schema():
-    BlogSchema = GraphQLSchema(BlogQuery)
-
-    assert BlogSchema.get_query_type() == BlogQuery
-
-    article_field = BlogQuery.fields["article"]
-    assert article_field.type == BlogArticle
-    assert article_field.type.name == "Article"
-    # assert article_field.name == 'article'
-
-    article_field_type = article_field.type
-    assert isinstance(article_field_type, GraphQLObjectType)
-
-    title_field = article_field_type.fields["title"]
-    # assert title_field.name == 'title'
-    assert title_field.type == GraphQLString
-    assert title_field.type.name == "String"
-
-    author_field = article_field_type.fields["author"]
-    author_field_type = author_field.type
-    assert isinstance(author_field_type, GraphQLObjectType)
-    recent_article_field = author_field_type.fields["recentArticle"]
-
-    assert recent_article_field.type == BlogArticle
-
-    feed_field = BlogQuery.fields["feed"]
-    assert feed_field.type.of_type == BlogArticle
-    # assert feed_field.name == 'feed'
-
-
-def test_defines_a_mutation_schema():
-    BlogSchema = GraphQLSchema(BlogQuery, BlogMutation)
-
-    assert BlogSchema.get_mutation_type() == BlogMutation
-
-    write_mutation = BlogMutation.fields["writeArticle"]
-    assert write_mutation.type == BlogArticle
-    assert write_mutation.type.name == "Article"
-    # assert write_mutation.name == 'writeArticle'
-
-
-def test_defines_a_subscription_schema():
-    BlogSchema = GraphQLSchema(query=BlogQuery, subscription=BlogSubscription)
-
-    assert BlogSchema.get_subscription_type() == BlogSubscription
-
-    subscription = BlogSubscription.fields["articleSubscribe"]
-    assert subscription.type == BlogArticle
-    assert subscription.type.name == "Article"
-    # assert subscription.name == 'articleSubscribe'
-
-
-def test_defines_an_enum_type_with_deprecated_value():
-    EnumTypeWithDeprecatedValue = GraphQLEnumType(
-        "EnumWithDeprecatedValue",
-        {"foo": GraphQLEnumValue(deprecation_reason="Just because")},
-    )
-    value = EnumTypeWithDeprecatedValue.get_values()[0]
-    assert value.name == "foo"
-    assert value.description is None
-    assert value.is_deprecated is True
-    assert value.deprecation_reason == "Just because"
-    assert value.value == "foo"
-
-
-def test_defines_an_enum_type_with_a_value_of_none():
-    EnumTypeWithNoneValue = GraphQLEnumType(
-        "EnumWithNullishValue", {"NULL": GraphQLEnumValue(None)}
-    )
-
-    value = EnumTypeWithNoneValue.get_values()[0]
-    assert value.name == "NULL"
-    assert value.description is None
-    assert value.is_deprecated is False
-    assert value.deprecation_reason is None
-    assert value.value is None
-
-
-def test_defines_an_object_type_with_deprecated_field():
-    TypeWithDeprecatedField = GraphQLObjectType(
-        "foo",
-        fields={
-            "bar": GraphQLField(
-                type=GraphQLString, deprecation_reason="A terrible reason"
-            )
-        },
-    )
-
-    field = TypeWithDeprecatedField.fields["bar"]
-    assert field.type == GraphQLString
-    assert field.description is None
-    assert field.deprecation_reason == "A terrible reason"
-    assert field.is_deprecated is True
-    # assert field.name == 'bar'
-    assert field.args == OrderedDict()
-
-
-def test_includes_nested_input_objects_in_the_map():
-    NestedInputObject = GraphQLInputObjectType(
-        name="NestedInputObject",
-        fields={"value": GraphQLInputObjectField(GraphQLString)},
-    )
-
-    SomeInputObject = GraphQLInputObjectType(
-        name="SomeInputObject",
-        fields={"nested": GraphQLInputObjectField(NestedInputObject)},
-    )
-
-    SomeMutation = GraphQLObjectType(
-        name="SomeMutation",
-        fields={
-            "mutateSomething": GraphQLField(
-                type=BlogArticle, args={"input": GraphQLArgument(SomeInputObject)}
-            )
-        },
-    )
-    SomeSubscription = GraphQLObjectType(
-        name="SomeSubscription",
-        fields={
-            "subscribeToSomething": GraphQLField(
-                type=BlogArticle, args={"input": GraphQLArgument(SomeInputObject)}
-            )
-        },
-    )
-
-    schema = GraphQLSchema(
-        query=BlogQuery, mutation=SomeMutation, subscription=SomeSubscription
-    )
-
-    assert schema.get_type_map()["NestedInputObject"] is NestedInputObject
-
-
-def test_includes_interface_possible_types_in_the_type_map():
-    SomeInterface = GraphQLInterfaceType(
-        "SomeInterface", fields={"f": GraphQLField(GraphQLInt)}
-    )
-    SomeSubtype = GraphQLObjectType(
-        name="SomeSubtype",
-        fields={"f": GraphQLField(GraphQLInt)},
-        interfaces=[SomeInterface],
-        is_type_of=lambda: None,
-    )
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query", fields={"iface": GraphQLField(SomeInterface)}
-        ),
-        types=[SomeSubtype],
-    )
-    assert schema.get_type_map()["SomeSubtype"] == SomeSubtype
-
-
-def test_includes_interfaces_thunk_subtypes_in_the_type_map():
-    SomeInterface = GraphQLInterfaceType(
-        name="SomeInterface", fields={"f": GraphQLField(GraphQLInt)}
-    )
-
-    SomeSubtype = GraphQLObjectType(
-        name="SomeSubtype",
-        fields={"f": GraphQLField(GraphQLInt)},
-        interfaces=lambda: [SomeInterface],
-        is_type_of=lambda: True,
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query", fields={"iface": GraphQLField(SomeInterface)}
-        ),
-        types=[SomeSubtype],
-    )
-
-    assert schema.get_type_map()["SomeSubtype"] is SomeSubtype
-
-
-def test_stringifies_simple_types():
-    assert str(GraphQLInt) == "Int"
-    assert str(BlogArticle) == "Article"
-    assert str(InterfaceType) == "Interface"
-    assert str(UnionType) == "Union"
-    assert str(EnumType) == "Enum"
-    assert str(InputObjectType) == "InputObject"
-    assert str(GraphQLNonNull(GraphQLInt)) == "Int!"
-    assert str(GraphQLList(GraphQLInt)) == "[Int]"
-    assert str(GraphQLNonNull(GraphQLList(GraphQLInt))) == "[Int]!"
-    assert str(GraphQLList(GraphQLNonNull(GraphQLInt))) == "[Int!]"
-    assert str(GraphQLList(GraphQLList(GraphQLInt))) == "[[Int]]"
-
-
-def test_identifies_input_types():
-    expected = (
-        (GraphQLInt, True),
-        (ObjectType, False),
-        (InterfaceType, False),
-        (UnionType, False),
-        (EnumType, True),
-        (InputObjectType, True),
-    )
-
-    for type, answer in expected:
-        assert is_input_type(type) == answer
-        assert is_input_type(GraphQLList(type)) == answer
-        assert is_input_type(GraphQLNonNull(type)) == answer
-
-
-def test_identifies_output_types():
-    expected = (
-        (GraphQLInt, True),
-        (ObjectType, True),
-        (InterfaceType, True),
-        (UnionType, True),
-        (EnumType, True),
-        (InputObjectType, False),
-    )
-
-    for type, answer in expected:
-        assert is_output_type(type) == answer
-        assert is_output_type(GraphQLList(type)) == answer
-        assert is_output_type(GraphQLNonNull(type)) == answer
-
-
-def test_prohibits_nesting_nonnull_inside_nonnull():
-    with raises(Exception) as excinfo:
-        GraphQLNonNull(GraphQLNonNull(GraphQLInt))
-
-    assert "Can only create NonNull of a Nullable GraphQLType but got: Int!." in str(
-        excinfo.value
-    )
-
-
-def test_prohibits_putting_non_object_types_in_unions():
-    bad_union_types = [
-        GraphQLInt,
-        GraphQLNonNull(GraphQLInt),
-        GraphQLList(GraphQLInt),
-        InterfaceType,
-        UnionType,
-        EnumType,
-        InputObjectType,
-    ]
-    for x in bad_union_types:
-        with raises(Exception) as excinfo:
-            GraphQLSchema(
-                GraphQLObjectType(
-                    "Root",
-                    fields={"union": GraphQLField(GraphQLUnionType("BadUnion", [x]))},
-                )
-            )
-
-        assert "BadUnion may only contain Object types, it cannot contain: " + str(
-            x
-        ) + "." == str(excinfo.value)
-
-
-def test_does_not_mutate_passed_field_definitions():
-    fields = {
-        "field1": GraphQLField(GraphQLString),
-        "field2": GraphQLField(
-            GraphQLString, args={"id": GraphQLArgument(GraphQLString)}
-        ),
-    }
-
-    TestObject1 = GraphQLObjectType(name="Test1", fields=fields)
-    TestObject2 = GraphQLObjectType(name="Test1", fields=fields)
-
-    assert TestObject1.fields == TestObject2.fields
-    assert fields == {
-        "field1": GraphQLField(GraphQLString),
-        "field2": GraphQLField(
-            GraphQLString, args={"id": GraphQLArgument(GraphQLString)}
-        ),
-    }
-
-    input_fields = {
-        "field1": GraphQLInputObjectField(GraphQLString),
-        "field2": GraphQLInputObjectField(GraphQLString),
-    }
-
-    TestInputObject1 = GraphQLInputObjectType(name="Test1", fields=input_fields)
-    TestInputObject2 = GraphQLInputObjectType(name="Test2", fields=input_fields)
-
-    assert TestInputObject1.fields == TestInputObject2.fields
-
-    assert input_fields == {
-        "field1": GraphQLInputObjectField(GraphQLString),
-        "field2": GraphQLInputObjectField(GraphQLString),
-    }
-
-
-# def test_sorts_fields_and_argument_keys_if_not_using_ordered_dict():
-#     fields = {
-#         'b': GraphQLField(GraphQLString),
-#         'c': GraphQLField(GraphQLString),
-#         'a': GraphQLField(GraphQLString),
-#         'd': GraphQLField(GraphQLString, args={
-#             'q': GraphQLArgument(GraphQLString),
-#             'x': GraphQLArgument(GraphQLString),
-#             'v': GraphQLArgument(GraphQLString),
-#             'a': GraphQLArgument(GraphQLString),
-#             'n': GraphQLArgument(GraphQLString)
-#         })
-#     }
-
-#     test_object = GraphQLObjectType(name='Test', fields=fields)
-#     ordered_fields = test_object.fields
-#     assert list(ordered_fields.keys()) == ['a', 'b', 'c', 'd']
-#     field_with_args = test_object.fields.get('d')
-#     assert list(field_with_args.args.keys()) == ['a', 'n', 'q', 'v', 'x']
-
-
-def test_does_not_sort_fields_and_argument_keys_when_using_ordered_dict():
-    fields = OrderedDict(
-        [
-            ("b", GraphQLField(GraphQLString)),
-            ("c", GraphQLField(GraphQLString)),
-            ("a", GraphQLField(GraphQLString)),
-            (
-                "d",
-                GraphQLField(
-                    GraphQLString,
-                    args=OrderedDict(
-                        [
-                            ("q", GraphQLArgument(GraphQLString)),
-                            ("x", GraphQLArgument(GraphQLString)),
-                            ("v", GraphQLArgument(GraphQLString)),
-                            ("a", GraphQLArgument(GraphQLString)),
-                            ("n", GraphQLArgument(GraphQLString)),
-                        ]
-                    ),
-                ),
-            ),
-        ]
-    )
-
-    test_object = GraphQLObjectType(name="Test", fields=fields)
-    ordered_fields = test_object.fields
-    assert list(ordered_fields.keys()) == ["b", "c", "a", "d"]
-    field_with_args = test_object.fields.get("d")
-    assert list(field_with_args.args.keys()) == ["q", "x", "v", "a", "n"]
diff --git a/graphql/type/tests/test_enum_type.py b/graphql/type/tests/test_enum_type.py
deleted file mode 100644
index 989fac8..0000000
--- a/graphql/type/tests/test_enum_type.py
+++ /dev/null
@@ -1,293 +0,0 @@
-from collections import OrderedDict
-from rx import Observable
-from graphql import graphql
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLInt,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-
-ColorType = GraphQLEnumType(
-    name="Color",
-    values=OrderedDict(
-        [
-            ("RED", GraphQLEnumValue(0)),
-            ("GREEN", GraphQLEnumValue(1)),
-            ("BLUE", GraphQLEnumValue(2)),
-        ]
-    ),
-)
-
-
-def get_first(args, *keys):
-    for key in keys:
-        if key in args:
-            return args[key]
-
-    return None
-
-
-QueryType = GraphQLObjectType(
-    name="Query",
-    fields={
-        "colorEnum": GraphQLField(
-            type=ColorType,
-            args={
-                "fromEnum": GraphQLArgument(ColorType),
-                "fromInt": GraphQLArgument(GraphQLInt),
-                "fromString": GraphQLArgument(GraphQLString),
-            },
-            resolver=lambda value, info, **args: get_first(
-                args, "fromInt", "fromString", "fromEnum"
-            ),
-        ),
-        "colorInt": GraphQLField(
-            type=GraphQLInt,
-            args={
-                "fromEnum": GraphQLArgument(ColorType),
-                "fromInt": GraphQLArgument(GraphQLInt),
-            },
-            resolver=lambda value, info, **args: get_first(args, "fromInt", "fromEnum"),
-        ),
-    },
-)
-
-MutationType = GraphQLObjectType(
-    name="Mutation",
-    fields={
-        "favoriteEnum": GraphQLField(
-            type=ColorType,
-            args={"color": GraphQLArgument(ColorType)},
-            resolver=lambda value, info, **args: args.get("color"),
-        )
-    },
-)
-
-SubscriptionType = GraphQLObjectType(
-    name="Subscription",
-    fields={
-        "subscribeToEnum": GraphQLField(
-            type=ColorType,
-            args={"color": GraphQLArgument(ColorType)},
-            resolver=lambda value, info, **args: Observable.from_([args.get("color")]),
-        )
-    },
-)
-
-Schema = GraphQLSchema(
-    query=QueryType, mutation=MutationType, subscription=SubscriptionType
-)
-
-
-def test_accepts_enum_literals_as_input():
-    result = graphql(Schema, "{ colorInt(fromEnum: GREEN) }")
-    assert not result.errors
-    assert result.data == {"colorInt": 1}
-
-
-def test_enum_may_be_output_type():
-    result = graphql(Schema, "{ colorEnum(fromInt: 1) }")
-    assert not result.errors
-    assert result.data == {"colorEnum": "GREEN"}
-
-
-def test_enum_may_be_both_input_and_output_type():
-    result = graphql(Schema, "{ colorEnum(fromEnum: GREEN) }")
-
-    assert not result.errors
-    assert result.data == {"colorEnum": "GREEN"}
-
-
-def test_does_not_accept_string_literals():
-    result = graphql(Schema, '{ colorEnum(fromEnum: "GREEN") }')
-    assert not result.data
-    assert (
-        result.errors[0].message == 'Argument "fromEnum" has invalid value "GREEN".\n'
-        'Expected type "Color", found "GREEN".'
-    )
-
-
-def test_does_not_accept_values_not_in_the_enum():
-    result = graphql(Schema, "{ colorEnum(fromEnum: GREENISH) }")
-    assert not result.data
-    assert (
-        result.errors[0].message == 'Argument "fromEnum" has invalid value GREENISH.\n'
-        'Expected type "Color", found GREENISH.'
-    )
-
-
-def test_does_not_accept_values_with_incorrect_casing():
-    result = graphql(Schema, "{ colorEnum(fromEnum: green) }")
-    assert not result.data
-    assert (
-        result.errors[0].message == 'Argument "fromEnum" has invalid value green.\n'
-        'Expected type "Color", found green.'
-    )
-
-
-def test_does_not_accept_incorrect_internal_value():
-    result = graphql(Schema, '{ colorEnum(fromString: "GREEN") }')
-    assert result.data == {"colorEnum": None}
-    assert (
-        result.errors[0].message == 'Expected a value of type "Color" '
-        "but received: GREEN"
-    )
-
-
-def test_does_not_accept_internal_value_in_place_of_enum_literal():
-    result = graphql(Schema, "{ colorEnum(fromEnum: 1) }")
-    assert not result.data
-    assert (
-        result.errors[0].message == 'Argument "fromEnum" has invalid value 1.\n'
-        'Expected type "Color", found 1.'
-    )
-
-
-def test_does_not_accept_enum_literal_in_place_of_int():
-    result = graphql(Schema, "{ colorEnum(fromInt: GREEN) }")
-    assert not result.data
-    assert (
-        result.errors[0].message == 'Argument "fromInt" has invalid value GREEN.\n'
-        'Expected type "Int", found GREEN.'
-    )
-
-
-def test_accepts_json_string_as_enum_variable():
-    result = graphql(
-        Schema,
-        "query test($color: Color!) { colorEnum(fromEnum: $color) }",
-        variable_values={"color": "BLUE"},
-    )
-    assert not result.errors
-    assert result.data == {"colorEnum": "BLUE"}
-
-
-def test_accepts_enum_literals_as_input_arguments_to_mutations():
-    result = graphql(
-        Schema,
-        "mutation x($color: Color!) { favoriteEnum(color: $color) }",
-        variable_values={"color": "GREEN"},
-    )
-    assert not result.errors
-    assert result.data == {"favoriteEnum": "GREEN"}
-
-
-def test_accepts_enum_literals_as_input_arguments_to_subscriptions():
-    result = graphql(
-        Schema,
-        "subscription x($color: Color!) { subscribeToEnum(color: $color) }",
-        variable_values={"color": "GREEN"},
-        allow_subscriptions=True,
-    )
-    assert isinstance(result, Observable)
-    l = []
-    result.subscribe(l.append)
-    result = l[0]
-    assert not result.errors
-    assert result.data == {"subscribeToEnum": "GREEN"}
-
-
-def test_does_not_accept_internal_value_as_enum_variable():
-    result = graphql(
-        Schema,
-        "query test($color: Color!) { colorEnum(fromEnum: $color) }",
-        variable_values={"color": 2},
-    )
-    assert not result.data
-    assert (
-        result.errors[0].message == 'Variable "$color" got invalid value 2.\n'
-        'Expected type "Color", found 2.'
-    )
-
-
-def test_does_not_accept_string_variables_as_enum_input():
-    result = graphql(
-        Schema,
-        "query test($color: String!) { colorEnum(fromEnum: $color) }",
-        variable_values={"color": "BLUE"},
-    )
-    assert not result.data
-    assert (
-        result.errors[0].message
-        == 'Variable "color" of type "String!" used in position expecting type "Color".'
-    )
-
-
-def test_does_not_accept_internal_value_as_enum_input():
-    result = graphql(
-        Schema,
-        "query test($color: Int!) { colorEnum(fromEnum: $color) }",
-        variable_values={"color": 2},
-    )
-    assert not result.data
-    assert (
-        result.errors[0].message
-        == 'Variable "color" of type "Int!" used in position expecting type "Color".'
-    )
-
-
-def test_enum_value_may_have_an_internal_value_of_0():
-    result = graphql(Schema, "{ colorEnum(fromEnum: RED) colorInt(fromEnum: RED) }")
-    assert not result.errors
-    assert result.data == {"colorEnum": "RED", "colorInt": 0}
-
-
-def test_enum_inputs_may_be_nullable():
-    result = graphql(Schema, "{ colorEnum colorInt }")
-    assert not result.errors
-    assert result.data == {"colorEnum": None, "colorInt": None}
-
-
-def test_presents_a_get_values_api():
-    values = ColorType.get_values()
-    assert len(values) == 3
-    assert values[0].name == "RED"
-    assert values[0].value == 0
-    assert values[1].name == "GREEN"
-    assert values[1].value == 1
-    assert values[2].name == "BLUE"
-    assert values[2].value == 2
-
-
-def test_presents_a_get_value_api():
-    oneValue = ColorType.get_value("RED")
-    assert oneValue.name == "RED"
-    assert oneValue.value == 0
-
-    badUsage = ColorType.get_value(0)
-    assert badUsage is None
-
-
-def test_sorts_values_if_not_using_ordered_dict():
-    enum = GraphQLEnumType(
-        name="Test",
-        values={
-            "c": GraphQLEnumValue(),
-            "b": GraphQLEnumValue(),
-            "a": GraphQLEnumValue(),
-            "d": GraphQLEnumValue(),
-        },
-    )
-
-    assert [v.name for v in enum.values] == ["a", "b", "c", "d"]
-
-
-def test_does_not_sort_values_when_using_ordered_dict():
-    enum = GraphQLEnumType(
-        name="Test",
-        values=OrderedDict(
-            [
-                ("c", GraphQLEnumValue()),
-                ("b", GraphQLEnumValue()),
-                ("a", GraphQLEnumValue()),
-                ("d", GraphQLEnumValue()),
-            ]
-        ),
-    )
-
-    assert [v.name for v in enum.values] == ["c", "b", "a", "d"]
diff --git a/graphql/type/tests/test_introspection.py b/graphql/type/tests/test_introspection.py
deleted file mode 100644
index 35df33b..0000000
--- a/graphql/type/tests/test_introspection.py
+++ /dev/null
@@ -1,1164 +0,0 @@
-import json
-from collections import OrderedDict
-
-from graphql import graphql
-from graphql.error import format_error
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLList,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-from graphql.utils.introspection_query import introspection_query
-from graphql.validation.rules import ProvidedNonNullArguments
-
-from ...pyutils.contain_subset import contain_subset
-
-
-def test_executes_an_introspection_query():
-    EmptySchema = GraphQLSchema(
-        GraphQLObjectType("QueryRoot", {"f": GraphQLField(GraphQLString)})
-    )
-
-    result = graphql(EmptySchema, introspection_query)
-    assert not result.errors
-    expected = {
-        "__schema": {
-            "mutationType": None,
-            "subscriptionType": None,
-            "queryType": {"name": "QueryRoot"},
-            "types": [
-                {
-                    "kind": "OBJECT",
-                    "name": "QueryRoot",
-                    "inputFields": None,
-                    "interfaces": [],
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "OBJECT",
-                    "name": "__Schema",
-                    "fields": [
-                        {
-                            "name": "types",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "LIST",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "NON_NULL",
-                                        "name": None,
-                                        "ofType": {"kind": "OBJECT", "name": "__Type"},
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "queryType",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "OBJECT",
-                                    "name": "__Type",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "mutationType",
-                            "args": [],
-                            "type": {
-                                "kind": "OBJECT",
-                                "name": "__Type",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "subscriptionType",
-                            "args": [],
-                            "type": {
-                                "kind": "OBJECT",
-                                "name": "__Type",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "directives",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "LIST",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "NON_NULL",
-                                        "name": None,
-                                        "ofType": {
-                                            "kind": "OBJECT",
-                                            "name": "__Directive",
-                                        },
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                    ],
-                    "inputFields": None,
-                    "interfaces": [],
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "OBJECT",
-                    "name": "__Type",
-                    "fields": [
-                        {
-                            "name": "kind",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "ENUM",
-                                    "name": "__TypeKind",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "name",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "description",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "fields",
-                            "args": [
-                                {
-                                    "name": "includeDeprecated",
-                                    "type": {
-                                        "kind": "SCALAR",
-                                        "name": "Boolean",
-                                        "ofType": None,
-                                    },
-                                    "defaultValue": "false",
-                                }
-                            ],
-                            "type": {
-                                "kind": "LIST",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "NON_NULL",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "OBJECT",
-                                        "name": "__Field",
-                                        "ofType": None,
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "interfaces",
-                            "args": [],
-                            "type": {
-                                "kind": "LIST",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "NON_NULL",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "OBJECT",
-                                        "name": "__Type",
-                                        "ofType": None,
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "possibleTypes",
-                            "args": [],
-                            "type": {
-                                "kind": "LIST",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "NON_NULL",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "OBJECT",
-                                        "name": "__Type",
-                                        "ofType": None,
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "enumValues",
-                            "args": [
-                                {
-                                    "name": "includeDeprecated",
-                                    "type": {
-                                        "kind": "SCALAR",
-                                        "name": "Boolean",
-                                        "ofType": None,
-                                    },
-                                    "defaultValue": "false",
-                                }
-                            ],
-                            "type": {
-                                "kind": "LIST",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "NON_NULL",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "OBJECT",
-                                        "name": "__EnumValue",
-                                        "ofType": None,
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "inputFields",
-                            "args": [],
-                            "type": {
-                                "kind": "LIST",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "NON_NULL",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "OBJECT",
-                                        "name": "__InputValue",
-                                        "ofType": None,
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "ofType",
-                            "args": [],
-                            "type": {
-                                "kind": "OBJECT",
-                                "name": "__Type",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                    ],
-                    "inputFields": None,
-                    "interfaces": [],
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "ENUM",
-                    "name": "__TypeKind",
-                    "fields": None,
-                    "inputFields": None,
-                    "interfaces": None,
-                    "enumValues": [
-                        {
-                            "name": "SCALAR",
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "OBJECT",
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "INTERFACE",
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "UNION",
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "ENUM",
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "INPUT_OBJECT",
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "LIST",
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "NON_NULL",
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                    ],
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "SCALAR",
-                    "name": "String",
-                    "fields": None,
-                    "inputFields": None,
-                    "interfaces": None,
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "SCALAR",
-                    "name": "Boolean",
-                    "fields": None,
-                    "inputFields": None,
-                    "interfaces": None,
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "OBJECT",
-                    "name": "__Field",
-                    "fields": [
-                        {
-                            "name": "name",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "String",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "description",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "args",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "LIST",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "NON_NULL",
-                                        "name": None,
-                                        "ofType": {
-                                            "kind": "OBJECT",
-                                            "name": "__InputValue",
-                                        },
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "type",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "OBJECT",
-                                    "name": "__Type",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "isDeprecated",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "Boolean",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "deprecationReason",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                    ],
-                    "inputFields": None,
-                    "interfaces": [],
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "OBJECT",
-                    "name": "__InputValue",
-                    "fields": [
-                        {
-                            "name": "name",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "String",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "description",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "type",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "OBJECT",
-                                    "name": "__Type",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "defaultValue",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                    ],
-                    "inputFields": None,
-                    "interfaces": [],
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "OBJECT",
-                    "name": "__EnumValue",
-                    "fields": [
-                        {
-                            "name": "name",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "String",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "description",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "isDeprecated",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "Boolean",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "deprecationReason",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                    ],
-                    "inputFields": None,
-                    "interfaces": [],
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "OBJECT",
-                    "name": "__Directive",
-                    "fields": [
-                        {
-                            "name": "name",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "String",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "description",
-                            "args": [],
-                            "type": {
-                                "kind": "SCALAR",
-                                "name": "String",
-                                "ofType": None,
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "locations",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "LIST",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "NON_NULL",
-                                        "name": None,
-                                        "ofType": {
-                                            "kind": "ENUM",
-                                            "name": "__DirectiveLocation",
-                                        },
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "args",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "LIST",
-                                    "name": None,
-                                    "ofType": {
-                                        "kind": "NON_NULL",
-                                        "name": None,
-                                        "ofType": {
-                                            "kind": "OBJECT",
-                                            "name": "__InputValue",
-                                        },
-                                    },
-                                },
-                            },
-                            "isDeprecated": False,
-                            "deprecationReason": None,
-                        },
-                        {
-                            "name": "onOperation",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "Boolean",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": True,
-                            "deprecationReason": "Use `locations`.",
-                        },
-                        {
-                            "name": "onFragment",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "Boolean",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": True,
-                            "deprecationReason": "Use `locations`.",
-                        },
-                        {
-                            "name": "onField",
-                            "args": [],
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "Boolean",
-                                    "ofType": None,
-                                },
-                            },
-                            "isDeprecated": True,
-                            "deprecationReason": "Use `locations`.",
-                        },
-                    ],
-                    "inputFields": None,
-                    "interfaces": [],
-                    "enumValues": None,
-                    "possibleTypes": None,
-                },
-                {
-                    "kind": "ENUM",
-                    "name": "__DirectiveLocation",
-                    "fields": None,
-                    "inputFields": None,
-                    "interfaces": None,
-                    "enumValues": [
-                        {"name": "QUERY", "isDeprecated": False},
-                        {"name": "MUTATION", "isDeprecated": False},
-                        {"name": "SUBSCRIPTION", "isDeprecated": False},
-                        {"name": "FIELD", "isDeprecated": False},
-                        {"name": "FRAGMENT_DEFINITION", "isDeprecated": False},
-                        {"name": "FRAGMENT_SPREAD", "isDeprecated": False},
-                        {"name": "INLINE_FRAGMENT", "isDeprecated": False},
-                    ],
-                    "possibleTypes": None,
-                },
-            ],
-            "directives": [
-                {
-                    "name": "include",
-                    "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
-                    "args": [
-                        {
-                            "defaultValue": None,
-                            "name": "if",
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "Boolean",
-                                    "ofType": None,
-                                },
-                            },
-                        }
-                    ],
-                },
-                {
-                    "name": "skip",
-                    "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
-                    "args": [
-                        {
-                            "defaultValue": None,
-                            "name": "if",
-                            "type": {
-                                "kind": "NON_NULL",
-                                "name": None,
-                                "ofType": {
-                                    "kind": "SCALAR",
-                                    "name": "Boolean",
-                                    "ofType": None,
-                                },
-                            },
-                        }
-                    ],
-                },
-            ],
-        }
-    }
-    assert contain_subset(expected, result.data)
-
-
-def test_introspects_on_input_object():
-    TestInputObject = GraphQLInputObjectType(
-        "TestInputObject",
-        OrderedDict(
-            [
-                ("a", GraphQLInputObjectField(GraphQLString, default_value="foo")),
-                ("b", GraphQLInputObjectField(GraphQLList(GraphQLString))),
-            ]
-        ),
-    )
-    TestType = GraphQLObjectType(
-        "TestType",
-        {
-            "field": GraphQLField(
-                type=GraphQLString,
-                args={"complex": GraphQLArgument(TestInputObject)},
-                resolver=lambda obj, info, **args: json.dumps(args.get("complex")),
-            )
-        },
-    )
-    schema = GraphQLSchema(TestType)
-    request = """
-      {
-        __schema {
-          types {
-            kind
-            name
-            inputFields {
-              name
-              type { ...TypeRef }
-              defaultValue
-            }
-          }
-        }
-      }
-      fragment TypeRef on __Type {
-        kind
-        name
-        ofType {
-          kind
-          name
-          ofType {
-            kind
-            name
-            ofType {
-              kind
-              name
-            }
-          }
-        }
-      }
-    """
-    result = graphql(schema, request)
-    assert not result.errors
-    assert {
-        "kind": "INPUT_OBJECT",
-        "name": "TestInputObject",
-        "inputFields": [
-            {
-                "name": "a",
-                "type": {"kind": "SCALAR", "name": "String", "ofType": None},
-                "defaultValue": '"foo"',
-            },
-            {
-                "name": "b",
-                "type": {
-                    "kind": "LIST",
-                    "name": None,
-                    "ofType": {"kind": "SCALAR", "name": "String", "ofType": None},
-                },
-                "defaultValue": None,
-            },
-        ],
-    } in result.data["__schema"]["types"]
-
-
-def test_supports_the_type_root_field():
-    TestType = GraphQLObjectType("TestType", {"testField": GraphQLField(GraphQLString)})
-    schema = GraphQLSchema(TestType)
-    request = '{ __type(name: "TestType") { name } }'
-    result = execute(schema, parse(request), object())
-    assert not result.errors
-    assert result.data == {"__type": {"name": "TestType"}}
-
-
-def test_identifies_deprecated_fields():
-    TestType = GraphQLObjectType(
-        "TestType",
-        OrderedDict(
-            [
-                ("nonDeprecated", GraphQLField(GraphQLString)),
-                (
-                    "deprecated",
-                    GraphQLField(GraphQLString, deprecation_reason="Removed in 1.0"),
-                ),
-            ]
-        ),
-    )
-    schema = GraphQLSchema(TestType)
-    request = """{__type(name: "TestType") {
-        name
-        fields(includeDeprecated: true) {
-            name
-            isDeprecated
-            deprecationReason
-        }
-    } }"""
-    result = graphql(schema, request)
-    assert not result.errors
-    assert result.data == {
-        "__type": {
-            "name": "TestType",
-            "fields": [
-                {
-                    "name": "nonDeprecated",
-                    "isDeprecated": False,
-                    "deprecationReason": None,
-                },
-                {
-                    "name": "deprecated",
-                    "isDeprecated": True,
-                    "deprecationReason": "Removed in 1.0",
-                },
-            ],
-        }
-    }
-
-
-def test_respects_the_includedeprecated_parameter_for_fields():
-    TestType = GraphQLObjectType(
-        "TestType",
-        OrderedDict(
-            [
-                ("nonDeprecated", GraphQLField(GraphQLString)),
-                (
-                    "deprecated",
-                    GraphQLField(GraphQLString, deprecation_reason="Removed in 1.0"),
-                ),
-            ]
-        ),
-    )
-    schema = GraphQLSchema(TestType)
-    request = """{__type(name: "TestType") {
-        name
-        trueFields: fields(includeDeprecated: true) { name }
-        falseFields: fields(includeDeprecated: false) { name }
-        omittedFields: fields { name }
-    } }"""
-    result = graphql(schema, request)
-    assert not result.errors
-    assert result.data == {
-        "__type": {
-            "name": "TestType",
-            "trueFields": [{"name": "nonDeprecated"}, {"name": "deprecated"}],
-            "falseFields": [{"name": "nonDeprecated"}],
-            "omittedFields": [{"name": "nonDeprecated"}],
-        }
-    }
-
-
-def test_identifies_deprecated_enum_values():
-    TestEnum = GraphQLEnumType(
-        "TestEnum",
-        OrderedDict(
-            [
-                ("NONDEPRECATED", GraphQLEnumValue(0)),
-                (
-                    "DEPRECATED",
-                    GraphQLEnumValue(1, deprecation_reason="Removed in 1.0"),
-                ),
-                ("ALSONONDEPRECATED", GraphQLEnumValue(2)),
-            ]
-        ),
-    )
-    TestType = GraphQLObjectType("TestType", {"testEnum": GraphQLField(TestEnum)})
-    schema = GraphQLSchema(TestType)
-    request = """{__type(name: "TestEnum") {
-        name
-        enumValues(includeDeprecated: true) {
-            name
-            isDeprecated
-            deprecationReason
-        }
-    } }"""
-    result = graphql(schema, request)
-    assert not result.errors
-    assert result.data == {
-        "__type": {
-            "name": "TestEnum",
-            "enumValues": [
-                {
-                    "name": "NONDEPRECATED",
-                    "isDeprecated": False,
-                    "deprecationReason": None,
-                },
-                {
-                    "name": "DEPRECATED",
-                    "isDeprecated": True,
-                    "deprecationReason": "Removed in 1.0",
-                },
-                {
-                    "name": "ALSONONDEPRECATED",
-                    "isDeprecated": False,
-                    "deprecationReason": None,
-                },
-            ],
-        }
-    }
-
-
-def test_respects_the_includedeprecated_parameter_for_enum_values():
-    TestEnum = GraphQLEnumType(
-        "TestEnum",
-        OrderedDict(
-            [
-                ("NONDEPRECATED", GraphQLEnumValue(0)),
-                (
-                    "DEPRECATED",
-                    GraphQLEnumValue(1, deprecation_reason="Removed in 1.0"),
-                ),
-                ("ALSONONDEPRECATED", GraphQLEnumValue(2)),
-            ]
-        ),
-    )
-    TestType = GraphQLObjectType("TestType", {"testEnum": GraphQLField(TestEnum)})
-    schema = GraphQLSchema(TestType)
-    request = """{__type(name: "TestEnum") {
-        name
-        trueValues: enumValues(includeDeprecated: true) { name }
-        falseValues: enumValues(includeDeprecated: false) { name }
-        omittedValues: enumValues { name }
-    } }"""
-    result = graphql(schema, request)
-    assert not result.errors
-    assert result.data == {
-        "__type": {
-            "name": "TestEnum",
-            "trueValues": [
-                {"name": "NONDEPRECATED"},
-                {"name": "DEPRECATED"},
-                {"name": "ALSONONDEPRECATED"},
-            ],
-            "falseValues": [{"name": "NONDEPRECATED"}, {"name": "ALSONONDEPRECATED"}],
-            "omittedValues": [{"name": "NONDEPRECATED"}, {"name": "ALSONONDEPRECATED"}],
-        }
-    }
-
-
-def test_fails_as_expected_on_the_type_root_field_without_an_arg():
-    TestType = GraphQLObjectType("TestType", {"testField": GraphQLField(GraphQLString)})
-    schema = GraphQLSchema(TestType)
-    request = """
-    {
-        __type {
-           name
-        }
-    }"""
-    result = graphql(schema, request)
-    expected_error = {
-        "message": ProvidedNonNullArguments.missing_field_arg_message(
-            "__type", "name", "String!"
-        ),
-        "locations": [dict(line=3, column=9)],
-    }
-    assert expected_error in [format_error(error) for error in result.errors]
-
-
-def test_exposes_descriptions_on_types_and_fields():
-    QueryRoot = GraphQLObjectType("QueryRoot", {"f": GraphQLField(GraphQLString)})
-    schema = GraphQLSchema(QueryRoot)
-    request = """{
-      schemaType: __type(name: "__Schema") {
-          name,
-          description,
-          fields {
-            name,
-            description
-          }
-        }
-      }
-    """
-    result = graphql(schema, request)
-    assert not result.errors
-    assert result.data == {
-        "schemaType": {
-            "name": "__Schema",
-            "description": "A GraphQL Schema defines the capabilities of a "
-            + "GraphQL server. It exposes all available types and "
-            + "directives on the server, as well as the entry "
-            + "points for query, mutation and subscription operations.",
-            "fields": [
-                {
-                    "name": "types",
-                    "description": "A list of all types supported by this server.",
-                },
-                {
-                    "name": "queryType",
-                    "description": "The type that query operations will be rooted at.",
-                },
-                {
-                    "name": "mutationType",
-                    "description": "If this server supports mutation, the type that "
-                    "mutation operations will be rooted at.",
-                },
-                {
-                    "name": "subscriptionType",
-                    "description": "If this server support subscription, the type "
-                    "that subscription operations will be rooted at.",
-                },
-                {
-                    "name": "directives",
-                    "description": "A list of all directives supported by this server.",
-                },
-            ],
-        }
-    }
-
-
-def test_exposes_descriptions_on_enums():
-    QueryRoot = GraphQLObjectType("QueryRoot", {"f": GraphQLField(GraphQLString)})
-    schema = GraphQLSchema(QueryRoot)
-    request = """{
-      typeKindType: __type(name: "__TypeKind") {
-          name,
-          description,
-          enumValues {
-            name,
-            description
-          }
-        }
-      }
-    """
-    result = graphql(schema, request)
-    assert not result.errors
-    assert result.data == {
-        "typeKindType": {
-            "name": "__TypeKind",
-            "description": "An enum describing what kind of type a given `__Type` is",
-            "enumValues": [
-                {"description": "Indicates this type is a scalar.", "name": "SCALAR"},
-                {
-                    "description": "Indicates this type is an object. "
-                    + "`fields` and `interfaces` are valid fields.",
-                    "name": "OBJECT",
-                },
-                {
-                    "description": "Indicates this type is an interface. "
-                    + "`fields` and `possibleTypes` are valid fields.",
-                    "name": "INTERFACE",
-                },
-                {
-                    "description": "Indicates this type is a union. "
-                    + "`possibleTypes` is a valid field.",
-                    "name": "UNION",
-                },
-                {
-                    "description": "Indicates this type is an enum. "
-                    + "`enumValues` is a valid field.",
-                    "name": "ENUM",
-                },
-                {
-                    "description": "Indicates this type is an input object. "
-                    + "`inputFields` is a valid field.",
-                    "name": "INPUT_OBJECT",
-                },
-                {
-                    "description": "Indicates this type is a list. "
-                    + "`ofType` is a valid field.",
-                    "name": "LIST",
-                },
-                {
-                    "description": "Indicates this type is a non-null. "
-                    + "`ofType` is a valid field.",
-                    "name": "NON_NULL",
-                },
-            ],
-        }
-    }
diff --git a/graphql/type/tests/test_schema.py b/graphql/type/tests/test_schema.py
deleted file mode 100644
index 5f7b56d..0000000
--- a/graphql/type/tests/test_schema.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from pytest import raises
-
-from ...type import (
-    GraphQLField,
-    GraphQLInterfaceType,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-
-interface_type = GraphQLInterfaceType(
-    name="Interface",
-    fields={
-        "field_name": GraphQLField(
-            type=GraphQLString, resolver=lambda *_: implementing_type
-        )
-    },
-)
-
-implementing_type = GraphQLObjectType(
-    name="Object",
-    interfaces=[interface_type],
-    fields={"field_name": GraphQLField(type=GraphQLString, resolver=lambda *_: "")},
-)
-
-
-schema = GraphQLSchema(
-    query=GraphQLObjectType(
-        name="Query",
-        fields={
-            "get_object": GraphQLField(type=interface_type, resolver=lambda *_: {})
-        },
-    )
-)
-
-
-def test_throws_human_readable_error_if_schematypes_not_defined():
-    with raises(AssertionError) as exci:
-        schema.is_possible_type(interface_type, implementing_type)
-
-    assert str(exci.value) == (
-        "Could not find possible implementing types for Interface in schema. Check that "
-        "schema.types is defined and is an array ofall possible types in the schema."
-    )
diff --git a/graphql/type/tests/test_serialization.py b/graphql/type/tests/test_serialization.py
deleted file mode 100644
index d91fd29..0000000
--- a/graphql/type/tests/test_serialization.py
+++ /dev/null
@@ -1,83 +0,0 @@
-import pytest
-
-from ..scalars import GraphQLBoolean, GraphQLFloat, GraphQLInt, GraphQLString
-from ..definition import GraphQLEnumType, GraphQLEnumValue
-from ...pyutils.compat import Enum
-
-
-def test_serializes_output_int():
-    assert GraphQLInt.serialize(1) == 1
-    assert GraphQLInt.serialize(0) == 0
-    assert GraphQLInt.serialize(-1) == -1
-    assert GraphQLInt.serialize(0.1) == 0
-    assert GraphQLInt.serialize(1.1) == 1
-    assert GraphQLInt.serialize(-1.1) == -1
-    assert GraphQLInt.serialize(1e5) == 100000
-    with pytest.raises(Exception):
-        GraphQLInt.serialize(9876504321)
-    with pytest.raises(Exception):
-        GraphQLInt.serialize(-9876504321)
-    with pytest.raises(Exception):
-        GraphQLInt.serialize(1e100)
-    with pytest.raises(Exception):
-        GraphQLInt.serialize(-1e100)
-    assert GraphQLInt.serialize("-1.1") == -1
-    with pytest.raises(Exception):
-        GraphQLInt.serialize("one")
-    assert GraphQLInt.serialize(False) == 0
-    assert GraphQLInt.serialize(True) == 1
-
-
-def test_serializes_output_float():
-    assert GraphQLFloat.serialize(1) == 1.0
-    assert GraphQLFloat.serialize(0) == 0.0
-    assert GraphQLFloat.serialize(-1) == -1.0
-    assert GraphQLFloat.serialize(0.1) == 0.1
-    assert GraphQLFloat.serialize(1.1) == 1.1
-    assert GraphQLFloat.serialize(-1.1) == -1.1
-    assert GraphQLFloat.serialize("-1.1") == -1.1
-    with pytest.raises(Exception):
-        GraphQLFloat.serialize("one")
-    assert GraphQLFloat.serialize(False) == 0
-    assert GraphQLFloat.serialize(True) == 1
-
-
-def test_serializes_output_string():
-    assert GraphQLString.serialize("string") == "string"
-    assert GraphQLString.serialize(1) == "1"
-    assert GraphQLString.serialize(-1.1) == "-1.1"
-    assert GraphQLString.serialize(True) == "true"
-    assert GraphQLString.serialize(False) == "false"
-    assert GraphQLString.serialize(u"\U0001F601") == u"\U0001F601"
-
-
-def test_serializes_output_boolean():
-    assert GraphQLBoolean.serialize("string") is True
-    assert GraphQLBoolean.serialize("") is False
-    assert GraphQLBoolean.serialize(1) is True
-    assert GraphQLBoolean.serialize(0) is False
-    assert GraphQLBoolean.serialize(True) is True
-    assert GraphQLBoolean.serialize(False) is False
-
-
-def test_serializes_enum():
-    class Color(Enum):
-        RED = "RED"
-        GREEN = "GREEN"
-        BLUE = "BLUE"
-        EXTRA = "EXTRA"
-
-    enum_type = GraphQLEnumType(
-        "Color",
-        values={
-            "RED": GraphQLEnumValue("RED"),
-            "GREEN": GraphQLEnumValue("GREEN"),
-            "BLUE": GraphQLEnumValue("BLUE"),
-        },
-    )
-    assert enum_type.serialize("RED") == "RED"
-    assert enum_type.serialize("NON_EXISTING") is None
-    assert enum_type.serialize(Color.RED) == "RED"
-    assert enum_type.serialize(Color.RED.value) == "RED"
-    assert enum_type.serialize(Color.EXTRA) is None
-    assert enum_type.serialize(Color.EXTRA.value) is None
diff --git a/graphql/type/tests/test_validation.py b/graphql/type/tests/test_validation.py
deleted file mode 100644
index 4bead70..0000000
--- a/graphql/type/tests/test_validation.py
+++ /dev/null
@@ -1,1717 +0,0 @@
-import re
-
-from pytest import raises
-
-from graphql.type import (
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-)
-from graphql.type.definition import GraphQLArgument
-
-_none = lambda *args: None
-_true = lambda *args: True
-_false = lambda *args: False
-
-SomeScalarType = GraphQLScalarType(
-    name="SomeScalar", serialize=_none, parse_value=_none, parse_literal=_none
-)
-
-SomeObjectType = GraphQLObjectType(
-    name="SomeObject", fields={"f": GraphQLField(GraphQLString)}
-)
-
-ObjectWithIsTypeOf = GraphQLObjectType(
-    name="ObjectWithIsTypeOf",
-    is_type_of=_true,
-    fields={"f": GraphQLField(GraphQLString)},
-)
-
-SomeUnionType = GraphQLUnionType(
-    name="SomeUnion", resolve_type=_none, types=[SomeObjectType]
-)
-
-SomeInterfaceType = GraphQLInterfaceType(
-    name="SomeInterface", resolve_type=_none, fields={"f": GraphQLField(GraphQLString)}
-)
-
-SomeEnumType = GraphQLEnumType(name="SomeEnum", values={"ONLY": GraphQLEnumValue()})
-
-SomeInputObjectType = GraphQLInputObjectType(
-    name="SomeInputObject",
-    fields={"val": GraphQLInputObjectField(GraphQLString, default_value="hello")},
-)
-
-
-def with_modifiers(types):
-    return (
-        types
-        + [GraphQLList(t) for t in types]
-        + [GraphQLNonNull(t) for t in types]
-        + [GraphQLNonNull(GraphQLList(t)) for t in types]
-    )
-
-
-output_types = with_modifiers(
-    [
-        GraphQLString,
-        SomeScalarType,
-        SomeEnumType,
-        SomeObjectType,
-        SomeUnionType,
-        SomeInterfaceType,
-    ]
-)
-
-not_output_types = with_modifiers([SomeInputObjectType]) + [str]
-
-input_types = with_modifiers(
-    [GraphQLString, SomeScalarType, SomeEnumType, SomeInputObjectType]
-)
-
-not_input_types = with_modifiers([SomeObjectType, SomeUnionType, SomeInterfaceType]) + [
-    str
-]
-
-
-def schema_with_field_type(t):
-    return GraphQLSchema(
-        query=GraphQLObjectType(name="Query", fields={"f": GraphQLField(t)}), types=[t]
-    )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ASchemaMustHaveObjectRootTypes:
-    def test_accepts_a_schema_whose_query_type_is_an_object_type(self):
-        assert GraphQLSchema(query=SomeObjectType)
-
-    def test_accepts_a_schema_whose_query_and_mutation_types_are_object_types(self):
-        MutationType = GraphQLObjectType(
-            name="Mutation", fields={"edit": GraphQLField(GraphQLString)}
-        )
-
-        assert GraphQLSchema(query=SomeObjectType, mutation=MutationType)
-
-    def test_accepts_a_schema_whose_query_and_subscription_types_are_object_types(self):
-        SubscriptionType = GraphQLObjectType(
-            name="Subscription", fields={"subscribe": GraphQLField(GraphQLString)}
-        )
-
-        assert GraphQLSchema(query=SomeObjectType, subscription=SubscriptionType)
-
-    def test_rejects_a_schema_without_a_query_type(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLSchema(query=None)
-
-        assert str(excinfo.value) == "Schema query must be Object Type but got: None."
-
-    def test_rejects_a_schema_whose_query_type_is_an_input_type(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLSchema(query=SomeInputObjectType)
-
-        assert (
-            str(excinfo.value)
-            == "Schema query must be Object Type but got: SomeInputObject."
-        )
-
-    def test_rejects_a_schema_whose_mutation_type_is_an_input_type(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLSchema(query=SomeObjectType, mutation=SomeInputObjectType)
-
-        assert (
-            str(excinfo.value)
-            == "Schema mutation must be Object Type but got: SomeInputObject."
-        )
-
-    def test_rejects_a_schema_whose_subscription_type_is_an_input_type(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLSchema(query=SomeObjectType, subscription=SomeInputObjectType)
-
-        assert (
-            str(excinfo.value)
-            == "Schema subscription must be Object Type but got: SomeInputObject."
-        )
-
-    def test_rejects_a_schema_whose_directives_are_incorrectly_typed(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLSchema(query=SomeObjectType, directives=["somedirective"])
-
-        assert (
-            str(excinfo.value)
-            == "Schema directives must be List[GraphQLDirective] if provided but got: "
-            "['somedirective']."
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ASchemaMustContainUniquelyNamedTypes:
-    def test_it_rejects_a_schema_which_defines_a_builtin_type(self):
-        FakeString = GraphQLScalarType(name="String", serialize=_none)
-
-        QueryType = GraphQLObjectType(
-            name="Query",
-            fields={
-                "normal": GraphQLField(GraphQLString),
-                "fake": GraphQLField(FakeString),
-            },
-        )
-
-        with raises(AssertionError) as excinfo:
-            GraphQLSchema(query=QueryType)
-
-        assert (
-            str(excinfo.value)
-            == "Schema must contain unique named types but contains multiple types named "
-            '"String".'
-        )
-
-    # noinspection PyUnusedLocal
-    def test_it_rejects_a_schema_which_have_same_named_objects_implementing_an_interface(
-        self
-    ):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"f": GraphQLField(GraphQLString)},
-        )
-
-        FirstBadObject = GraphQLObjectType(
-            name="BadObject",
-            interfaces=[AnotherInterface],
-            fields={"f": GraphQLField(GraphQLString)},
-        )
-
-        SecondBadObject = GraphQLObjectType(
-            name="BadObject",
-            interfaces=[AnotherInterface],
-            fields={"f": GraphQLField(GraphQLString)},
-        )
-
-        QueryType = GraphQLObjectType(
-            name="Query", fields={"iface": GraphQLField(AnotherInterface)}
-        )
-
-        with raises(AssertionError) as excinfo:
-            GraphQLSchema(query=QueryType, types=[FirstBadObject, SecondBadObject])
-
-        assert (
-            str(excinfo.value)
-            == "Schema must contain unique named types but contains multiple types named "
-            '"BadObject".'
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ObjectsMustHaveFields:
-    def test_accepts_an_object_type_with_fields_object(self):
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject", fields={"f": GraphQLField(GraphQLString)}
-            )
-        )
-
-    def test_accepts_an_object_type_with_a_field_function(self):
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject", fields=lambda: {"f": GraphQLField(GraphQLString)}
-            )
-        )
-
-    def test_rejects_an_object_type_with_missing_fields(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(GraphQLObjectType(name="SomeObject", fields=None))
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject fields must be a mapping (dict / OrderedDict) with field names "
-            "as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_object_type_with_incorrectly_named_fields(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(
-                    name="SomeObject",
-                    fields={"bad-name-with-dashes": GraphQLField(GraphQLString)},
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "bad-name-with-dashes" does not.'
-        )
-
-    def test_rejects_an_object_type_with_incorrectly_typed_fields(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(
-                    name="SomeObject", fields=[GraphQLField(GraphQLString)]
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject fields must be a mapping (dict / OrderedDict) with field names "
-            "as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_object_type_with_empty_fields(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(GraphQLObjectType(name="SomeObject", fields={}))
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject fields must be a mapping (dict / OrderedDict) with field names "
-            "as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_object_type_with_a_field_function_that_returns_nothing(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(GraphQLObjectType(name="SomeObject", fields=_none))
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject fields must be a mapping (dict / OrderedDict) with field names "
-            "as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_object_type_with_a_field_function_that_returns_empty(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(name="SomeObject", fields=lambda: {})
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject fields must be a mapping (dict / OrderedDict) with field names "
-            "as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_object_type_with_a_field_with_an_invalid_value(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(name="SomeObject", fields={"f": "hello"})
-            )
-
-        assert str(excinfo.value) == "SomeObject.f must be an instance of GraphQLField."
-
-    def test_rejects_an_object_type_with_a_field_function_with_an_invalid_value(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(name="SomeObject", fields=lambda: {"f": "hello"})
-            )
-
-        assert str(excinfo.value) == "SomeObject.f must be an instance of GraphQLField."
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_FieldArgsMustBeProperlyNamed:
-    def test_accepts_field_args_with_valid_names(self):
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject",
-                fields={
-                    "goodField": GraphQLField(
-                        GraphQLString, args={"goodArg": GraphQLArgument(GraphQLString)}
-                    )
-                },
-            )
-        )
-
-    def test_reject_field_args_with_invalid_names(self):
-        with raises(AssertionError) as excinfo:
-            assert schema_with_field_type(
-                GraphQLObjectType(
-                    name="SomeObject",
-                    fields={
-                        "badField": GraphQLField(
-                            GraphQLString,
-                            args={
-                                "bad-name-with-dashes": GraphQLArgument(GraphQLString)
-                            },
-                        )
-                    },
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "bad-name-with-dashes" does not.'
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_FieldArgsMustBeObjects:
-    def test_accepts_an_object_with_field_args(self):
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject",
-                fields={
-                    "goodField": GraphQLField(
-                        GraphQLString, args={"goodArg": GraphQLArgument(GraphQLString)}
-                    )
-                },
-            )
-        )
-
-    def test_rejects_an_object_with_incorrectly_typed_field_args(self):
-        with raises(AssertionError) as excinfo:
-            assert schema_with_field_type(
-                GraphQLObjectType(
-                    name="SomeObject",
-                    fields={
-                        "badField": GraphQLField(
-                            GraphQLString, args=[GraphQLArgument(GraphQLString)]
-                        )
-                    },
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject.badField args must be a mapping (dict / OrderedDict) with argument "
-            "names as keys."
-        )
-
-    def test_rejects_an_object_with_incorrectly_typed_field_args_with_an_invalid_value(
-        self
-    ):
-        with raises(AssertionError) as excinfo:
-            assert schema_with_field_type(
-                GraphQLObjectType(
-                    name="SomeObject",
-                    fields={
-                        "badField": GraphQLField(
-                            GraphQLString, args={"badArg": "I am bad!"}
-                        )
-                    },
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject.badField(badArg:) argument must be an instance of GraphQLArgument."
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ObjectInterfacesMustBeArray:
-    def test_accepts_an_object_type_with_array_interface(self):
-        AnotherInterfaceType = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"f": GraphQLField(GraphQLString)},
-        )
-
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject",
-                interfaces=[AnotherInterfaceType],
-                fields={"f": GraphQLField(GraphQLString)},
-            )
-        )
-
-    def test_accepts_an_object_type_with_interfaces_as_a_function_returning_an_array(
-        self
-    ):
-        AnotherInterfaceType = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"f": GraphQLField(GraphQLString)},
-        )
-
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject",
-                interfaces=lambda: [AnotherInterfaceType],
-                fields={"f": GraphQLField(GraphQLString)},
-            )
-        )
-
-    def test_rejects_an_object_type_with_incorrectly_typed_interfaces(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(
-                    name="SomeObject",
-                    interfaces={},
-                    fields={"f": GraphQLField(GraphQLString)},
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject interfaces must be a list/tuple or a function which returns a "
-            "list/tuple."
-        )
-
-    def test_rejects_an_object_type_with_interfaces_as_a_function_returning_an_incorrect_type(
-        self
-    ):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(
-                    name="SomeObject",
-                    interfaces=lambda: {},
-                    fields={"f": GraphQLField(GraphQLString)},
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeObject interfaces must be a list/tuple or a function which returns a "
-            "list/tuple."
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_UnionTypesMustBeArray:
-    def test_accepts_a_union_type_with_aray_types(self):
-        assert schema_with_field_type(
-            GraphQLUnionType(
-                name="SomeUnion", resolve_type=_none, types=[SomeObjectType]
-            )
-        )
-
-    def test_rejects_a_union_without_types(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLUnionType(name="SomeUnion", resolve_type=_none, types=[])
-            )
-
-        assert str(excinfo.value) == "Must provide types for Union SomeUnion."
-
-    def test_rejects_a_union_type_with_empty_types(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLUnionType(name="SomeUnion", resolve_type=_none, types=[])
-            )
-
-        assert str(excinfo.value) == "Must provide types for Union SomeUnion."
-
-    def test_rejects_a_union_type_with_incorrectly_typed_types(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLUnionType(
-                    name="SomeUnion",
-                    resolve_type=_none,
-                    types={"SomeObjectType": SomeObjectType},
-                )
-            )
-
-        assert str(excinfo.value) == "Must provide types for Union SomeUnion."
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_UnionTypesMustBeCallableThatReturnsArray:
-    def test_accepts_a_union_type_with_aray_types(self):
-        assert schema_with_field_type(
-            GraphQLUnionType(
-                name="SomeUnion", resolve_type=_none, types=lambda: [SomeObjectType]
-            )
-        )
-
-    def test_rejects_a_union_type_with_empty_types(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLUnionType(name="SomeUnion", resolve_type=_none, types=lambda: [])
-            )
-
-        assert str(excinfo.value) == "Must provide types for Union SomeUnion."
-
-    def test_rejects_a_union_type_with_incorrectly_typed_types(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLUnionType(
-                    name="SomeUnion",
-                    resolve_type=_none,
-                    types=lambda: {"SomeObjectType": SomeObjectType},
-                )
-            )
-
-        assert str(excinfo.value) == "Must provide types for Union SomeUnion."
-
-
-def schema_with_input_object(input_object_type):
-    return GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "f": GraphQLField(
-                    GraphQLString, args={"badArg": GraphQLArgument(input_object_type)}
-                )
-            },
-        )
-    )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_InputObjectsMustHaveFields:
-    def test_accepts_an_input_object_type_with_fields(self):
-        assert schema_with_input_object(
-            GraphQLInputObjectType(
-                name="SomeInputObject",
-                fields={"f": GraphQLInputObjectField(GraphQLString)},
-            )
-        )
-
-    def test_accepts_an_input_object_type_with_field_function(self):
-        assert schema_with_input_object(
-            GraphQLInputObjectType(
-                name="SomeInputObject",
-                fields=lambda: {"f": GraphQLInputObjectField(GraphQLString)},
-            )
-        )
-
-    def test_rejects_an_input_object_type_with_missing_fields(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_input_object(
-                GraphQLInputObjectType(name="SomeInputObject", fields=None)
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeInputObject fields must be a mapping (dict / OrderedDict) with "
-            "field names as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_input_object_type_with_incorrectly_typed_fields(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_input_object(
-                GraphQLInputObjectType(
-                    name="SomeInputObject",
-                    fields=[GraphQLInputObjectField(GraphQLString)],
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeInputObject fields must be a mapping (dict / OrderedDict) with "
-            "field names as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_input_object_type_with_incorrectly_typed_field_value(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_input_object(
-                GraphQLInputObjectType(
-                    name="SomeInputObject", fields={"f": GraphQLField(GraphQLString)}
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeInputObject.f must be an instance of GraphQLInputObjectField."
-        )
-
-    def test_rejects_an_input_object_type_with_fields_function_returning_incorrectly_typed_field_value(
-        self
-    ):
-        with raises(AssertionError) as excinfo:
-            schema_with_input_object(
-                GraphQLInputObjectType(
-                    name="SomeInputObject",
-                    fields=lambda: {"f": GraphQLField(GraphQLString)},
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeInputObject.f must be an instance of GraphQLInputObjectField."
-        )
-
-    def test_rejects_an_input_object_type_with_empty_fields(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_input_object(
-                GraphQLInputObjectType(name="SomeInputObject", fields={})
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeInputObject fields must be a mapping (dict / OrderedDict) with "
-            "field names as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_input_object_type_with_a_field_function_that_returns_nothing(
-        self
-    ):
-        with raises(AssertionError) as excinfo:
-            schema_with_input_object(
-                GraphQLInputObjectType(name="SomeInputObject", fields=_none)
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeInputObject fields must be a mapping (dict / OrderedDict) with "
-            "field names as keys or a function which returns such a mapping."
-        )
-
-    def test_rejects_an_input_object_type_with_a_field_function_that_returns_empty(
-        self
-    ):
-        with raises(AssertionError) as excinfo:
-            schema_with_input_object(
-                GraphQLInputObjectType(name="SomeInputObject", fields=lambda: {})
-            )
-
-        assert (
-            str(excinfo.value)
-            == "SomeInputObject fields must be a mapping (dict / OrderedDict) with "
-            "field names as keys or a function which returns such a mapping."
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ObjectTypesMustBeAssertable:
-    def test_accepts_an_object_type_with_an_is_type_of_function(self):
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="AnotherObject",
-                is_type_of=_true,
-                fields={"f": GraphQLField(GraphQLString)},
-            )
-        )
-
-    def test_rejects_an_object_type_with_an_incorrect_type_for_is_type_of(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(
-                    name="AnotherObject",
-                    is_type_of={},
-                    fields={"f": GraphQLField(GraphQLString)},
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'AnotherObject must provide "is_type_of" as a function.'
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_InterfaceTypesMustBeResolvable:
-    def test_accepts_an_interface_type_defining_resolve_type(self):
-        AnotherInterfaceType = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"f": GraphQLField(GraphQLString)},
-        )
-
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject",
-                interfaces=[AnotherInterfaceType],
-                fields={"f": GraphQLField(GraphQLString)},
-            )
-        )
-
-    def test_accepts_an_interface_with_implementing_type_defining_is_type_of(self):
-        AnotherInterfaceType = GraphQLInterfaceType(
-            name="AnotherInterface", fields={"f": GraphQLField(GraphQLString)}
-        )
-
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject",
-                is_type_of=_true,
-                interfaces=[AnotherInterfaceType],
-                fields={"f": GraphQLField(GraphQLString)},
-            )
-        )
-
-    def test_accepts_an_interface_type_defining_resolve_type_with_implementing_type_defining_is_type_of(
-        self
-    ):
-        AnotherInterfaceType = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"f": GraphQLField(GraphQLString)},
-        )
-        assert schema_with_field_type(
-            GraphQLObjectType(
-                name="SomeObject",
-                is_type_of=_true,
-                interfaces=[AnotherInterfaceType],
-                fields={"f": GraphQLField(GraphQLString)},
-            )
-        )
-
-    def test_rejects_an_interface_type_with_an_incorrect_type_for_resolve_type(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLInterfaceType(
-                name="AnotherInterface",
-                resolve_type={},
-                fields={"f": GraphQLField(GraphQLString)},
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'AnotherInterface must provide "resolve_type" as a function.'
-        )
-
-    def test_rejects_an_interface_type_not_defining_resolve_type_with_implementing_type_not_defining_is_type_of(
-        self
-    ):
-        AnotherInterfaceType = GraphQLInterfaceType(
-            name="AnotherInterface", fields={"f": GraphQLField(GraphQLString)}
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLObjectType(
-                    name="SomeObject",
-                    interfaces=[AnotherInterfaceType],
-                    fields={"f": GraphQLField(GraphQLString)},
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'Interface Type AnotherInterface does not provide a "resolve_type" function and '
-            'implementing Type SomeObject does not provide a "is_type_of" function. '
-            "There is no way to resolve this implementing type during execution."
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_UnionTypesMustBeResolvable:
-    def test_accepts_a_union_type_defining_resolve_type(self):
-        assert schema_with_field_type(
-            GraphQLUnionType(
-                name="SomeUnion", resolve_type=_none, types=[SomeObjectType]
-            )
-        )
-
-    def test_accepts_a_union_of_object_types_defining_is_type_of(self):
-        assert schema_with_field_type(
-            GraphQLUnionType(name="SomeUnion", types=[ObjectWithIsTypeOf])
-        )
-
-    def test_accepts_a_union_type_defning_resolve_type_of_objects_defning_is_type_of(
-        self
-    ):
-        assert schema_with_field_type(
-            GraphQLUnionType(
-                name="SomeUnion", resolve_type=_none, types=[ObjectWithIsTypeOf]
-            )
-        )
-
-    def test_rejects_an_interface_type_with_an_incorrect_type_for_resolve_type(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLUnionType(
-                    name="SomeUnion", resolve_type={}, types=[ObjectWithIsTypeOf]
-                )
-            )
-
-        assert (
-            str(excinfo.value) == 'SomeUnion must provide "resolve_type" as a function.'
-        )
-
-    def test_rejects_a_union_type_not_defining_resolve_type_of_object_types_not_defining_is_type_of(
-        self
-    ):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLUnionType(name="SomeUnion", types=[SomeObjectType])
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'Union Type SomeUnion does not provide a "resolve_type" function and possible '
-            'Type SomeObject does not provide a "is_type_of" function. '
-            "There is no way to resolve this possible type during execution."
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ScalarTypesMustBeSerializable:
-    def test_accepts_a_scalar_type_defining_serialize(self):
-        assert schema_with_field_type(
-            GraphQLScalarType(name="SomeScalar", serialize=_none)
-        )
-
-    def test_rejects_a_scalar_type_not_defining_serialize(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(GraphQLScalarType(name="SomeScalar"))
-
-        assert (
-            str(excinfo.value)
-            == 'SomeScalar must provide "serialize" function. If this custom Scalar is also '
-            'used as an input type, ensure "parse_value" and "parse_literal" '
-            "functions are also provided."
-        )
-
-    def test_rejects_a_scalar_type_defining_serialize_with_an_incorrect_type(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(GraphQLScalarType(name="SomeScalar", serialize={}))
-
-        assert (
-            str(excinfo.value)
-            == 'SomeScalar must provide "serialize" function. If this custom Scalar is also '
-            'used as an input type, ensure "parse_value" and "parse_literal" '
-            "functions are also provided."
-        )
-
-    def test_accepts_scalar_type_defining_parse_value_and_parse_literal(self):
-        assert schema_with_field_type(
-            GraphQLScalarType(
-                name="SomeScalar",
-                serialize=_none,
-                parse_literal=_none,
-                parse_value=_none,
-            )
-        )
-
-    def test_rejects_a_scalar_type_defining_parse_value_but_not_parse_literal(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLScalarType(name="SomeScalar", serialize=_none, parse_value=_none)
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'SomeScalar must provide both "parse_value" and "parse_literal" functions.'
-        )
-
-    def test_rejects_a_scalar_type_defining_parse_literal_but_not_parse_value(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLScalarType(
-                    name="SomeScalar", serialize=_none, parse_literal=_none
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'SomeScalar must provide both "parse_value" and "parse_literal" functions.'
-        )
-
-    def test_rejects_a_scalar_type_defining_parse_literal_and_parse_value_with_an_incorrect_type(
-        self
-    ):
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(
-                GraphQLScalarType(
-                    name="SomeScalar", serialize=_none, parse_literal={}, parse_value={}
-                )
-            )
-
-        assert (
-            str(excinfo.value)
-            == 'SomeScalar must provide both "parse_value" and "parse_literal" functions.'
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_EnumTypesMustBeWellDefined:
-    def test_accepts_a_well_defined_enum_type_with_empty_value_definition(self):
-        assert GraphQLEnumType(
-            name="SomeEnum",
-            values={"FOO": GraphQLEnumValue(), "BAR": GraphQLEnumValue()},
-        )
-
-    def test_accepts_a_well_defined_enum_type_with_internal_value_definition(self):
-        assert GraphQLEnumType(
-            name="SomeEnum",
-            values={"FOO": GraphQLEnumValue(10), "BAR": GraphQLEnumValue(20)},
-        )
-
-    def test_rejects_an_enum_without_values(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLEnumType(name="SomeEnum", values=None)
-
-        assert (
-            str(excinfo.value)
-            == "SomeEnum values must be a mapping (dict / OrderedDict) with value names as keys."
-        )
-
-    def test_rejects_an_enum_with_empty_values(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLEnumType(name="SomeEnum", values={})
-        assert (
-            str(excinfo.value)
-            == "SomeEnum values must be a mapping (dict / OrderedDict) with value names as keys."
-        )
-
-    def test_rejects_an_enum_with_incorrectly_typed_values(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLEnumType(name="SomeEnum", values=[{"foo": GraphQLEnumValue(10)}])
-
-        assert (
-            str(excinfo.value)
-            == "SomeEnum values must be a mapping (dict / OrderedDict) with value names as keys."
-        )
-
-    def test_rejects_an_enum_with_missing_value_definition(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLEnumType(name="SomeEnum", values={"FOO": None})
-        assert (
-            str(excinfo.value)
-            == "SomeEnum.FOO must be an instance of GraphQLEnumValue, but got: None"
-        )
-
-    def test_rejects_an_enum_with_incorrectly_typed_value_definition(self):
-        with raises(AssertionError) as excinfo:
-            GraphQLEnumType(name="SomeEnum", values={"FOO": 10})
-        assert (
-            str(excinfo.value)
-            == "SomeEnum.FOO must be an instance of GraphQLEnumValue, but got: 10"
-        )
-
-
-def schema_with_object_field_of_type(field_type):
-    BadObjectType = GraphQLObjectType(
-        name="BadObject", fields={"badField": GraphQLField(field_type)}
-    )
-
-    return schema_with_field_type(BadObjectType)
-
-
-def repr_type_as_syntax_safe_fn(_type):
-    if isinstance(_type, GraphQLList):
-        return "list_" + repr_type_as_syntax_safe_fn(_type.of_type)
-
-    if isinstance(_type, GraphQLNonNull):
-        return "non_null_" + repr_type_as_syntax_safe_fn(_type.of_type)
-
-    return re.sub(r"[^a-zA-Z]", "_", str(_type)) + "_" + type(_type).__name__
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ObjectFieldsMustHaveOutputTypes:
-    def accepts(self, type):
-        assert schema_with_object_field_of_type(type)
-
-    for i, type in enumerate(output_types):
-        exec(
-            "def test_accepts_an_output_type_as_an_object_field_type_{}(self): self.accepts(output_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-    def test_rejects_an_empty_object_field_type(self):
-        with raises(AssertionError) as excinfo:
-            schema_with_object_field_of_type(None)
-
-        assert (
-            str(excinfo.value)
-            == "BadObject.badField field type must be Output Type but got: None."
-        )
-
-    def rejects(self, type):
-        with raises(AssertionError) as excinfo:
-            schema_with_object_field_of_type(type)
-
-        assert str(
-            excinfo.value
-        ) == "BadObject.badField field type must be Output Type but got: {}.".format(
-            type
-        )
-
-    for i, type in enumerate(not_output_types):
-        exec(
-            "def test_rejects_a_non_output_type_as_an_object_field_type_{}(self): self.rejects(not_output_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-
-def schema_with_object_implementing_type(implemented_type):
-    BadObjectType = GraphQLObjectType(
-        name="BadObject",
-        interfaces=[implemented_type],
-        fields={"f": GraphQLField(GraphQLString)},
-    )
-
-    return schema_with_field_type(BadObjectType)
-
-
-not_interface_types = with_modifiers(
-    [SomeScalarType, SomeEnumType, SomeObjectType, SomeUnionType, SomeInputObjectType]
-)
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ObjectsCanOnlyImplementInterfaces:
-    def test_accepts_an_object_implementing_an_interface(self):
-        AnotherInterfaceType = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"f": GraphQLField(GraphQLString)},
-        )
-
-        assert schema_with_object_implementing_type(AnotherInterfaceType)
-
-    def rejects(self, type):
-        with raises(AssertionError) as excinfo:
-            schema_with_object_implementing_type(type)
-
-        assert str(
-            excinfo.value
-        ) == "BadObject may only implement Interface types, it cannot implement: {}.".format(
-            type
-        )
-
-    for i, type in enumerate(not_interface_types):
-        exec(
-            "def test_rejects_an_object_implementing_a_non_interface_type_{}(self):"
-            " self.rejects(not_interface_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-
-not_object_types = with_modifiers(
-    [
-        SomeScalarType,
-        SomeEnumType,
-        SomeInterfaceType,
-        SomeUnionType,
-        SomeInputObjectType,
-    ]
-)
-
-
-def schema_with_union_of_type(type):
-    BadUnionType = GraphQLUnionType(name="BadUnion", resolve_type=_none, types=[type])
-
-    return schema_with_field_type(BadUnionType)
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_UnionMustRepresentObjectTypes:
-    def test_accepts_a_union_of_an_object_type(self):
-        assert schema_with_union_of_type(SomeObjectType)
-
-    def rejects(self, type):
-        with raises(AssertionError) as excinfo:
-            schema_with_union_of_type(type)
-
-        assert str(
-            excinfo.value
-        ) == "BadUnion may only contain Object types, it cannot contain: {}.".format(
-            type
-        )
-
-    for i, type in enumerate(not_object_types):
-        exec(
-            "def test_rejects_a_union_of_non_object_type_{}(self):"
-            " self.rejects(not_object_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-
-def schema_with_interface_field_of_type(field_type):
-    BadInterfaceType = GraphQLInterfaceType(
-        name="BadInterface", fields={"badField": GraphQLField(field_type)}
-    )
-
-    return schema_with_field_type(BadInterfaceType)
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_InterfaceFieldsMustHaveOutputTypes:
-    def accepts(self, type):
-        assert schema_with_interface_field_of_type(type)
-
-    def rejects(self, type):
-        with raises(AssertionError) as excinfo:
-            schema_with_interface_field_of_type(type)
-
-        assert str(
-            excinfo.value
-        ) == "BadInterface.badField field type must be Output Type but got: {}.".format(
-            type
-        )
-
-    for i, type in enumerate(output_types):
-        exec(
-            "def test_accepts_an_output_type_as_an_interface_field_type_{}(self):"
-            " self.accepts(output_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-    def test_rejects_an_empty_interface_field_type(self):
-        self.rejects(None)
-
-    for i, type in enumerate(not_output_types):
-        exec(
-            "def test_rejects_a_non_output_type_as_an_interface_field_type_{}(self):"
-            " self.rejects(not_output_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-def schema_with_arg_of_type(arg_type):
-    BadObjectType = GraphQLObjectType(
-        name="BadObject",
-        fields={
-            "badField": GraphQLField(
-                type=GraphQLString, args={"badArg": GraphQLArgument(arg_type)}
-            )
-        },
-    )
-
-    return schema_with_field_type(BadObjectType)
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_FieldArgumentsMustHaveInputTypes:
-    def accepts(self, type):
-        assert schema_with_arg_of_type(type)
-
-    def rejects(self, type):
-        with raises(AssertionError) as excinfo:
-            schema_with_arg_of_type(type)
-
-        assert str(
-            excinfo.value
-        ) == "BadObject.badField(badArg:) argument type must be Input " "Type but got: {}.".format(
-            type
-        )
-
-    for i, type in enumerate(input_types):
-        exec(
-            "def test_accepts_an_input_type_as_a_field_arg_type_{}(self):"
-            " self.accepts(input_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-    def test_rejects_an_empty_field_arg_type(self):
-        self.rejects(None)
-
-    for i, type in enumerate(not_input_types):
-        exec(
-            "def test_rejects_a_not_input_type_as_a_field_arg_type_{}(self):"
-            " self.rejects(not_input_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-
-def schema_with_input_field_of_type(input_field_type):
-    BadInputObjectType = GraphQLInputObjectType(
-        name="BadInputObject",
-        fields={"badField": GraphQLInputObjectField(input_field_type)},
-    )
-
-    return GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "f": GraphQLField(
-                    type=GraphQLString,
-                    args={"badArg": GraphQLArgument(BadInputObjectType)},
-                )
-            },
-        )
-    )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_InputObjectFieldsMustHaveInputTypes:
-    def accepts(self, type):
-        assert schema_with_input_field_of_type(type)
-
-    def rejects(self, type):
-        with raises(AssertionError) as excinfo:
-            schema_with_input_field_of_type(type)
-
-        assert str(
-            excinfo.value
-        ) == "BadInputObject.badField field type must be Input Type but got: {}.".format(
-            type
-        )
-
-    for i, type in enumerate(input_types):
-        exec(
-            "def test_accepts_an_input_type_as_an_input_field_type_{}(self):"
-            " self.accepts(input_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-    def test_rejects_an_empty_input_field_type(self):
-        self.rejects(None)
-
-    for i, type in enumerate(not_input_types):
-        exec(
-            "def test_rejects_non_input_type_as_an_input_field_type_{}(self):"
-            " self.rejects(not_input_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-
-types = with_modifiers(
-    [
-        GraphQLString,
-        SomeScalarType,
-        SomeObjectType,
-        SomeUnionType,
-        SomeInterfaceType,
-        SomeEnumType,
-        SomeInputObjectType,
-    ]
-)
-
-not_types = [{}, str, None, object(), set(), (), []]
-
-
-class TestTypeSystem_ListMustAcceptGraphQLTypes:
-    def accepts(self, type):
-        assert GraphQLList(type)
-
-    def rejects(self, type):
-        with raises(AssertionError) as excinfo:
-            GraphQLList(type)
-
-        assert str(
-            excinfo.value
-        ) == "Can only create List of a GraphQLType but got: {}.".format(type)
-
-    for i, type in enumerate(types):
-        exec(
-            "def test_accepts_a_type_as_item_type_of_list_{}(self):"
-            " self.accepts(types[{}])".format(repr_type_as_syntax_safe_fn(type), i)
-        )
-
-    for i, type in enumerate(not_types):
-        exec(
-            "def test_accepts_a_type_as_item_type_of_list_{}(self):"
-            " self.rejects(not_types[{}])".format(repr_type_as_syntax_safe_fn(type), i)
-        )
-
-
-nullable_types = [
-    GraphQLString,
-    SomeScalarType,
-    SomeObjectType,
-    SomeUnionType,
-    SomeInterfaceType,
-    SomeEnumType,
-    SomeInputObjectType,
-    GraphQLList(GraphQLString),
-    GraphQLList(GraphQLNonNull(GraphQLString)),
-]
-
-not_nullable_types = [GraphQLNonNull(GraphQLString), {}, str, None, []]
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_NonNullMustAcceptGraphQLTypes:
-    def accepts(self, type):
-        assert GraphQLNonNull(type)
-
-    def rejects(self, type):
-        with raises(AssertionError) as excinfo:
-            GraphQLNonNull(type)
-
-        assert str(
-            excinfo.value
-        ) == "Can only create NonNull of a Nullable GraphQLType but got: {}.".format(
-            type
-        )
-
-    for i, type in enumerate(nullable_types):
-        exec(
-            "def test_accepts_a_type_as_nullable_type_of_not_null_{}(self):"
-            " self.accepts(nullable_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-    for i, type in enumerate(not_nullable_types):
-        exec(
-            "def test_rejects_a_non_type_as_nullable_type_of_non_null_{}(self):"
-            " self.rejects(not_nullable_types[{}])".format(
-                repr_type_as_syntax_safe_fn(type), i
-            )
-        )
-
-
-# noinspection PyMethodMayBeStatic,PyPep8Naming
-class TestTypeSystem_ObjectsMustAdhereToInterfacesTheyImplement:
-    def test_accepts_an_object_which_implements_an_interface(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                )
-            },
-        )
-
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                )
-            },
-        )
-
-        assert schema_with_field_type(AnotherObject)
-
-    def test_accepts_an_object_which_implements_an_interface_along_with_more_fields(
-        self
-    ):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                )
-            },
-        )
-
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                ),
-                "anotherfield": GraphQLField(GraphQLString),
-            },
-        )
-
-        assert schema_with_field_type(AnotherObject)
-
-    def test_accepts_an_object_which_implements_an_interface_field_along_with_more_arguments(
-        self
-    ):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                )
-            },
-        )
-
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString,
-                    args={
-                        "input": GraphQLArgument(GraphQLString),
-                        "anotherInput": GraphQLArgument(GraphQLString),
-                    },
-                )
-            },
-        )
-
-        assert schema_with_field_type(AnotherObject)
-
-    def test_rejects_an_object_which_implements_an_interface_field_along_with_additional_required_arguments(
-        self
-    ):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                )
-            },
-        )
-
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString,
-                    args={
-                        "input": GraphQLArgument(GraphQLString),
-                        "anotherInput": GraphQLArgument(GraphQLNonNull(GraphQLString)),
-                    },
-                )
-            },
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value)
-            == 'AnotherObject.field(anotherInput:) is of required type "String!" but '
-            "is not also provided by the interface AnotherInterface.field."
-        )
-
-    def test_rejects_an_object_missing_an_interface_field(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={
-                "field": GraphQLField(
-                    type=GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                )
-            },
-        )
-
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"anotherfield": GraphQLField(GraphQLString)},
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value)
-            == '"AnotherInterface" expects field "field" but "AnotherObject" does not provide it.'
-        )
-
-    def test_rejects_an_object_with_an_incorrectly_typed_interface_field(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"field": GraphQLField(GraphQLString)},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(SomeScalarType)},
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value) == 'AnotherInterface.field expects type "String" '
-            'but AnotherObject.field provides type "SomeScalar".'
-        )
-
-    def test_rejects_an_object_missing_an_interface_argument(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={
-                "field": GraphQLField(
-                    GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                )
-            },
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(GraphQLString)},
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value) == 'AnotherInterface.field expects argument "input" '
-            "but AnotherObject.field does not provide it."
-        )
-
-    def test_rejects_an_object_with_an_incorrectly_typed_interface_argument(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={
-                "field": GraphQLField(
-                    GraphQLString, args={"input": GraphQLArgument(GraphQLString)}
-                )
-            },
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={
-                "field": GraphQLField(
-                    GraphQLString, args={"input": GraphQLArgument(SomeScalarType)}
-                )
-            },
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value)
-            == 'AnotherInterface.field(input:) expects type "String" '
-            'but AnotherObject.field(input:) provides type "SomeScalar".'
-        )
-
-    def test_rejects_an_object_with_an_incorrectly_typed_interface_field(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"field": GraphQLField(GraphQLString)},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(SomeScalarType)},
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value) == 'AnotherInterface.field expects type "String" '
-            'but AnotherObject.field provides type "SomeScalar".'
-        )
-
-    def test_rejects_an_object_with_a_differently_typed_Interface_field(self):
-        TypeA = GraphQLObjectType(name="A", fields={"foo": GraphQLField(GraphQLString)})
-        TypeB = GraphQLObjectType(name="B", fields={"foo": GraphQLField(GraphQLString)})
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"field": GraphQLField(TypeA)},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(TypeB)},
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value) == 'AnotherInterface.field expects type "A" but '
-            'AnotherObject.field provides type "B".'
-        )
-
-    def test_accepts_an_object_with_a_subtyped_interface_field_interface(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields=lambda: {"field": GraphQLField(AnotherInterface)},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields=lambda: {"field": GraphQLField(AnotherObject)},
-        )
-
-        assert schema_with_field_type(AnotherObject)
-
-    def test_accepts_an_object_with_a_subtyped_interface_field_union(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields=lambda: {"field": GraphQLField(SomeUnionType)},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields=lambda: {"field": GraphQLField(SomeObjectType)},
-        )
-
-        assert schema_with_field_type(AnotherObject)
-
-    def test_accepts_an_object_with_an_equivalently_modified_interface_field_type(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"field": GraphQLField(GraphQLNonNull(GraphQLList(GraphQLString)))},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(GraphQLNonNull(GraphQLList(GraphQLString)))},
-        )
-
-        assert schema_with_field_type(AnotherObject)
-
-    def test_rejects_an_object_with_a_non_list_interface_field_list_type(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"field": GraphQLField(GraphQLList(GraphQLString))},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(GraphQLString)},
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value) == 'AnotherInterface.field expects type "[String]" '
-            'but AnotherObject.field provides type "String".'
-        )
-
-    def test_rejects_a_object_with_a_list_interface_field_non_list_type(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"field": GraphQLField(GraphQLString)},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(GraphQLList(GraphQLString))},
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value) == 'AnotherInterface.field expects type "String" '
-            'but AnotherObject.field provides type "[String]".'
-        )
-
-    def test_accepts_an_object_with_a_subset_non_null_interface_field_type(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"field": GraphQLField(GraphQLString)},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(GraphQLNonNull(GraphQLString))},
-        )
-
-        assert schema_with_field_type(AnotherObject)
-
-    def test_rejects_a_object_with_a_superset_nullable_interface_field_type(self):
-        AnotherInterface = GraphQLInterfaceType(
-            name="AnotherInterface",
-            resolve_type=_none,
-            fields={"field": GraphQLField(GraphQLNonNull(GraphQLString))},
-        )
-        AnotherObject = GraphQLObjectType(
-            name="AnotherObject",
-            interfaces=[AnotherInterface],
-            fields={"field": GraphQLField(GraphQLString)},
-        )
-
-        with raises(AssertionError) as excinfo:
-            schema_with_field_type(AnotherObject)
-
-        assert (
-            str(excinfo.value) == 'AnotherInterface.field expects type "String!" but '
-            'AnotherObject.field provides type "String".'
-        )
diff --git a/graphql/type/typemap.py b/graphql/type/typemap.py
deleted file mode 100644
index 427417c..0000000
--- a/graphql/type/typemap.py
+++ /dev/null
@@ -1,208 +0,0 @@
-from collections import OrderedDict, defaultdict
-from functools import reduce
-
-from ..utils.type_comparators import is_equal_type, is_type_sub_type_of
-from .definition import (
-    GraphQLArgument,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLUnionType,
-    is_input_type,
-    is_output_type,
-)
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..type.definition import GraphQLNamedType
-    from typing import List, Union, Dict, Set, DefaultDict
-
-
-class GraphQLTypeMap(OrderedDict):
-    def __init__(self, types):
-        # type: (List[GraphQLNamedType]) -> None
-        super(GraphQLTypeMap, self).__init__()
-        self.update(reduce(self.reducer, types, OrderedDict()))  # type: ignore
-        self._possible_type_map = defaultdict(set)  # type: DefaultDict[str, Set[str]]
-
-        # Keep track of all implementations by interface name.
-        self._implementations = defaultdict(
-            list
-        )  # type: DefaultDict[str, List[GraphQLObjectType]]
-        for gql_type in self.values():
-            if isinstance(gql_type, GraphQLObjectType):
-                for interface in gql_type.interfaces:
-                    self._implementations[interface.name].append(gql_type)
-
-        # Enforce correct interface implementations.
-        for type in self.values():
-            if isinstance(type, GraphQLObjectType):
-                for interface in type.interfaces:
-                    self.assert_object_implements_interface(self, type, interface)
-
-    def get_possible_types(self, abstract_type):
-        # type: (Union[GraphQLInterfaceType, GraphQLUnionType]) -> List[GraphQLObjectType]
-        if isinstance(abstract_type, GraphQLUnionType):
-            return abstract_type.types
-        assert isinstance(abstract_type, GraphQLInterfaceType)
-        if abstract_type.name not in self._implementations:
-            return []
-        return self._implementations[abstract_type.name]
-
-    def is_possible_type(
-        self,
-        abstract_type,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-        possible_type,  # type: GraphQLObjectType
-    ):
-        # type: (...) -> bool
-        possible_types = self.get_possible_types(abstract_type)
-        assert possible_types, (
-            "Could not find possible implementing types for {} in "
-            + "schema. Check that schema.types is defined and is an array of"
-            + "all possible types in the schema."
-        ).format(abstract_type)
-
-        if abstract_type.name not in self._possible_type_map:
-            self._possible_type_map[abstract_type.name].update(
-                [p.name for p in possible_types]
-            )
-
-        return possible_type.name in self._possible_type_map[abstract_type.name]
-
-    @classmethod
-    def reducer(cls, map, type):
-        # type: (Dict, Union[GraphQLNamedType, GraphQLList, GraphQLNonNull]) -> Dict
-        if not type:
-            return map
-
-        if isinstance(type, (GraphQLList, GraphQLNonNull)):
-            return cls.reducer(map, type.of_type)
-
-        if type.name in map:
-            assert map[type.name] == type, (
-                'Schema must contain unique named types but contains multiple types named "{}".'
-            ).format(type.name)
-
-            return map
-
-        map[type.name] = type  # type: ignore
-
-        reduced_map = map
-
-        if isinstance(type, (GraphQLUnionType)):
-            for t in type.types:
-                reduced_map = cls.reducer(reduced_map, t)
-
-        if isinstance(type, GraphQLObjectType):
-            for t in type.interfaces:
-                reduced_map = cls.reducer(reduced_map, t)
-
-        if isinstance(
-            type, (GraphQLObjectType, GraphQLInterfaceType, GraphQLInputObjectType)
-        ):
-            field_map = type.fields
-            type_is_input = isinstance(type, GraphQLInputObjectType)
-            for field_name, field in field_map.items():
-                if type_is_input:
-                    assert isinstance(
-                        field, GraphQLInputObjectField
-                    ), "{}.{} must be an instance of GraphQLInputObjectField.".format(
-                        type, field_name
-                    )
-                    assert is_input_type(
-                        field.type
-                    ), "{}.{} field type must be Input Type but got: {}.".format(
-                        type, field_name, field.type
-                    )
-                else:
-                    assert is_output_type(
-                        field.type
-                    ), "{}.{} field type must be Output Type but got: {}.".format(
-                        type, field_name, field.type
-                    )
-                    for arg_name, arg in field.args.items():
-                        assert isinstance(
-                            arg, (GraphQLArgument, GraphQLArgument)
-                        ), "{}.{}({}:) argument must be an instance of GraphQLArgument.".format(
-                            type, field_name, arg_name
-                        )
-                        assert is_input_type(
-                            arg.type
-                        ), "{}.{}({}:) argument type must be Input Type but got: {}.".format(
-                            type, field_name, arg_name, arg.type
-                        )
-                        reduced_map = cls.reducer(reduced_map, arg.type)
-
-                reduced_map = cls.reducer(reduced_map, getattr(field, "type", None))
-
-        return reduced_map
-
-    @classmethod
-    def assert_object_implements_interface(
-        cls,
-        schema,  # type: GraphQLTypeMap
-        object,  # type: GraphQLObjectType
-        interface,  # type: GraphQLInterfaceType
-    ):
-        # type: (...) -> None
-        object_field_map = object.fields
-        interface_field_map = interface.fields
-
-        for field_name, interface_field in interface_field_map.items():
-            object_field = object_field_map.get(field_name)
-
-            assert (
-                object_field
-            ), '"{}" expects field "{}" but "{}" does not provide it.'.format(
-                interface, field_name, object
-            )
-
-            assert is_type_sub_type_of(
-                schema, object_field.type, interface_field.type
-            ), ('{}.{} expects type "{}" but {}.{} provides type "{}".').format(
-                interface,
-                field_name,
-                interface_field.type,
-                object,
-                field_name,
-                object_field.type,
-            )
-
-            for arg_name, interface_arg in interface_field.args.items():
-                object_arg = object_field.args.get(arg_name)
-
-                assert object_arg, (
-                    '{}.{} expects argument "{}" but {}.{} does not provide it.'
-                ).format(interface, field_name, arg_name, object, field_name)
-
-                assert is_equal_type(interface_arg.type, object_arg.type), (
-                    '{}.{}({}:) expects type "{}" but {}.{}({}:) provides type "{}".'
-                ).format(
-                    interface,
-                    field_name,
-                    arg_name,
-                    interface_arg.type,
-                    object,
-                    field_name,
-                    arg_name,
-                    object_arg.type,
-                )
-
-            for arg_name, object_arg in object_field.args.items():
-                interface_arg = interface_field.args.get(arg_name)
-                if not interface_arg:
-                    assert not isinstance(object_arg.type, GraphQLNonNull), (
-                        "{}.{}({}:) is of required type "
-                        '"{}" but is not also provided by the '
-                        "interface {}.{}."
-                    ).format(
-                        object,
-                        field_name,
-                        arg_name,
-                        object_arg.type,
-                        interface,
-                        field_name,
-                    )
diff --git a/graphql/utils/__init__.py b/graphql/utils/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/utils/assert_valid_name.py b/graphql/utils/assert_valid_name.py
deleted file mode 100644
index 2775b9e..0000000
--- a/graphql/utils/assert_valid_name.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import re
-
-NAME_PATTERN = r"^[_a-zA-Z][_a-zA-Z0-9]*$"
-COMPILED_NAME_PATTERN = re.compile(NAME_PATTERN)
-
-
-def assert_valid_name(name):
-    # type: (str) -> None
-    """Helper to assert that provided names are valid."""
-    assert COMPILED_NAME_PATTERN.match(
-        name
-    ), 'Names must match /{}/ but "{}" does not.'.format(NAME_PATTERN, name)
-    return None
diff --git a/graphql/utils/ast_from_value.py b/graphql/utils/ast_from_value.py
deleted file mode 100644
index 6773423..0000000
--- a/graphql/utils/ast_from_value.py
+++ /dev/null
@@ -1,70 +0,0 @@
-import json
-import re
-import sys
-
-from six import string_types
-
-from ..language import ast
-from ..type.definition import (
-    GraphQLEnumType,
-    GraphQLInputObjectType,
-    GraphQLList,
-    GraphQLNonNull,
-)
-from ..type.scalars import GraphQLFloat
-
-
-def ast_from_value(value, type=None):
-    if isinstance(type, GraphQLNonNull):
-        return ast_from_value(value, type.of_type)
-
-    if value is None:
-        return None
-
-    if isinstance(value, list):
-        item_type = type.of_type if isinstance(type, GraphQLList) else None
-        return ast.ListValue([ast_from_value(item, item_type) for item in value])
-
-    elif isinstance(type, GraphQLList):
-        return ast_from_value(value, type.of_type)
-
-    if isinstance(value, bool):
-        return ast.BooleanValue(value)
-
-    if isinstance(value, (int, float)):
-        string_num = str(value)
-        int_value = int(value)
-        is_int_value = string_num.isdigit()
-
-        if is_int_value or (int_value == value and value < sys.maxsize):
-            if type == GraphQLFloat:
-                return ast.FloatValue(str(float(value)))
-
-            return ast.IntValue(str(int(value)))
-
-        return ast.FloatValue(string_num)
-
-    if isinstance(value, string_types):
-        if isinstance(type, GraphQLEnumType) and re.match(
-            r"^[_a-zA-Z][_a-zA-Z0-9]*$", value
-        ):
-            return ast.EnumValue(value)
-
-        return ast.StringValue(json.dumps(value)[1:-1])
-
-    assert isinstance(value, dict)
-
-    fields = []
-    is_graph_ql_input_object_type = isinstance(type, GraphQLInputObjectType)
-
-    for field_name, field_value in value.items():
-        field_type = None
-        if is_graph_ql_input_object_type:
-            field_def = type.fields.get(field_name)
-            field_type = field_def and field_def.type
-
-        field_value = ast_from_value(field_value, field_type)
-        if field_value:
-            fields.append(ast.ObjectField(ast.Name(field_name), field_value))
-
-    return ast.ObjectValue(fields)
diff --git a/graphql/utils/ast_to_code.py b/graphql/utils/ast_to_code.py
deleted file mode 100644
index 07b8068..0000000
--- a/graphql/utils/ast_to_code.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from ..language.ast import Node
-from ..language.parser import Loc
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any
-
-
-def ast_to_code(ast, indent=0):
-    # type: (Any, int) -> str
-    """
-    Converts an ast into a python code representation of the AST.
-    """
-    code = []
-
-    def append(line):
-        # type: (str) -> None
-        code.append(("    " * indent) + line)
-
-    if isinstance(ast, Node):
-        append("ast.{}(".format(ast.__class__.__name__))
-        indent += 1
-        for i, k in enumerate(ast._fields, 1):
-            v = getattr(ast, k)
-            append("{}={},".format(k, ast_to_code(v, indent)))
-        if ast.loc:
-            append("loc={}".format(ast_to_code(ast.loc, indent)))
-
-        indent -= 1
-        append(")")
-
-    elif isinstance(ast, Loc):
-        append("loc({}, {})".format(ast.start, ast.end))
-
-    elif isinstance(ast, list):
-        if ast:
-            append("[")
-            indent += 1
-
-            for i, it in enumerate(ast, 1):
-                is_last = i == len(ast)
-                append(ast_to_code(it, indent) + ("," if not is_last else ""))
-
-            indent -= 1
-            append("]")
-        else:
-            append("[]")
-
-    else:
-        append(repr(ast))
-
-    return "\n".join(code).strip()
diff --git a/graphql/utils/ast_to_dict.py b/graphql/utils/ast_to_dict.py
deleted file mode 100644
index 57e6e92..0000000
--- a/graphql/utils/ast_to_dict.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from ..language.ast import Node
-
-
-def ast_to_dict(node, include_loc=False):
-    if isinstance(node, Node):
-        d = {"kind": node.__class__.__name__}
-        if hasattr(node, "_fields"):
-            for field in node._fields:
-                d[field] = ast_to_dict(getattr(node, field), include_loc)
-
-        if include_loc and hasattr(node, "loc") and node.loc:
-            d["loc"] = {"start": node.loc.start, "end": node.loc.end}
-
-        return d
-
-    elif isinstance(node, list):
-        return [ast_to_dict(item, include_loc) for item in node]
-
-    return node
diff --git a/graphql/utils/base.py b/graphql/utils/base.py
deleted file mode 100644
index 093b3f6..0000000
--- a/graphql/utils/base.py
+++ /dev/null
@@ -1,76 +0,0 @@
-"""
-    Base GraphQL utilities
-    isort:skip_file
-"""
-
-# The GraphQL query recommended for a full schema introspection.
-from .introspection_query import introspection_query
-
-# Gets the target Operation from a Document
-from .get_operation_ast import get_operation_ast
-
-# Build a GraphQLSchema from an introspection result.
-from .build_client_schema import build_client_schema
-
-# Build a GraphQLSchema from a parsed GraphQL Schema language AST.
-from .build_ast_schema import build_ast_schema
-
-# Extends an existing GraphQLSchema from a parsed GraphQL Schema language AST.
-from .extend_schema import extend_schema
-
-# Print a GraphQLSchema to GraphQL Schema language.
-from .schema_printer import print_schema, print_introspection_schema
-
-# Create a GraphQLType from a GraphQL language AST.
-from .type_from_ast import type_from_ast
-
-# Create a JavaScript value from a GraphQL language AST.
-from .value_from_ast import value_from_ast
-
-# Create a GraphQL language AST from a JavaScript value.
-from .ast_from_value import ast_from_value
-
-# A helper to use within recursive-descent visitors which need to be aware of
-# the GraphQL type system.
-from .type_info import TypeInfo
-
-# Determine if JavaScript values adhere to a GraphQL type.
-from .is_valid_value import is_valid_value
-
-# Determine if AST values adhere to a GraphQL type.
-from .is_valid_literal_value import is_valid_literal_value
-
-# Concatenates multiple AST together.
-from .concat_ast import concat_ast
-
-# Comparators for types
-from .type_comparators import is_equal_type, is_type_sub_type_of, do_types_overlap
-
-# Asserts that a string is a valid GraphQL name
-from .assert_valid_name import assert_valid_name
-
-# Undefined const
-from .undefined import Undefined
-
-
-__all__ = [
-    "introspection_query",
-    "get_operation_ast",
-    "build_client_schema",
-    "build_ast_schema",
-    "extend_schema",
-    "print_introspection_schema",
-    "print_schema",
-    "type_from_ast",
-    "value_from_ast",
-    "ast_from_value",
-    "TypeInfo",
-    "is_valid_value",
-    "is_valid_literal_value",
-    "concat_ast",
-    "do_types_overlap",
-    "is_equal_type",
-    "is_type_sub_type_of",
-    "assert_valid_name",
-    "Undefined",
-]
diff --git a/graphql/utils/build_ast_schema.py b/graphql/utils/build_ast_schema.py
deleted file mode 100644
index 8d28f4b..0000000
--- a/graphql/utils/build_ast_schema.py
+++ /dev/null
@@ -1,354 +0,0 @@
-from ..execution.values import get_argument_values
-from ..language import ast
-from ..pyutils.ordereddict import OrderedDict
-from ..type import (
-    GraphQLArgument,
-    GraphQLBoolean,
-    GraphQLDeprecatedDirective,
-    GraphQLDirective,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLFloat,
-    GraphQLID,
-    GraphQLIncludeDirective,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInt,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLSchema,
-    GraphQLSkipDirective,
-    GraphQLString,
-    GraphQLUnionType,
-)
-from ..type.introspection import (
-    __Directive,
-    __DirectiveLocation,
-    __EnumValue,
-    __Field,
-    __InputValue,
-    __Schema,
-    __Type,
-    __TypeKind,
-)
-from ..utils.value_from_ast import value_from_ast
-
-
-def _build_wrapped_type(inner_type, input_type_ast):
-    if isinstance(input_type_ast, ast.ListType):
-        return GraphQLList(_build_wrapped_type(inner_type, input_type_ast.type))
-
-    if isinstance(input_type_ast, ast.NonNullType):
-        return GraphQLNonNull(_build_wrapped_type(inner_type, input_type_ast.type))
-
-    return inner_type
-
-
-def _get_inner_type_name(type_ast):
-    if isinstance(type_ast, (ast.ListType, ast.NonNullType)):
-        return _get_inner_type_name(type_ast.type)
-
-    return type_ast.name.value
-
-
-def _get_named_type_ast(type_ast):
-    named_type = type_ast
-    while isinstance(named_type, (ast.ListType, ast.NonNullType)):
-        named_type = named_type.type
-
-    return named_type
-
-
-def _false(*_):
-    return False
-
-
-def _none(*_):
-    return None
-
-
-def build_ast_schema(document):
-    assert isinstance(document, ast.Document), "must pass in Document ast."
-
-    schema_def = None
-
-    type_asts = (
-        ast.ScalarTypeDefinition,
-        ast.ObjectTypeDefinition,
-        ast.InterfaceTypeDefinition,
-        ast.EnumTypeDefinition,
-        ast.UnionTypeDefinition,
-        ast.InputObjectTypeDefinition,
-    )
-
-    type_defs = []
-    directive_defs = []
-
-    for d in document.definitions:
-        if isinstance(d, ast.SchemaDefinition):
-            if schema_def:
-                raise Exception("Must provide only one schema definition.")
-            schema_def = d
-        if isinstance(d, type_asts):
-            type_defs.append(d)
-        elif isinstance(d, ast.DirectiveDefinition):
-            directive_defs.append(d)
-
-    if not schema_def:
-        raise Exception("Must provide a schema definition.")
-
-    query_type_name = None
-    mutation_type_name = None
-    subscription_type_name = None
-
-    for operation_type in schema_def.operation_types:
-        type_name = operation_type.type.name.value
-        if operation_type.operation == "query":
-            if query_type_name:
-                raise Exception("Must provide only one query type in schema.")
-            query_type_name = type_name
-        elif operation_type.operation == "mutation":
-            if mutation_type_name:
-                raise Exception("Must provide only one mutation type in schema.")
-            mutation_type_name = type_name
-        elif operation_type.operation == "subscription":
-            if subscription_type_name:
-                raise Exception("Must provide only one subscription type in schema.")
-            subscription_type_name = type_name
-
-    if not query_type_name:
-        raise Exception("Must provide schema definition with query type.")
-
-    ast_map = {d.name.value: d for d in type_defs}
-
-    if query_type_name not in ast_map:
-        raise Exception(
-            'Specified query type "{}" not found in document.'.format(query_type_name)
-        )
-
-    if mutation_type_name and mutation_type_name not in ast_map:
-        raise Exception(
-            'Specified mutation type "{}" not found in document.'.format(
-                mutation_type_name
-            )
-        )
-
-    if subscription_type_name and subscription_type_name not in ast_map:
-        raise Exception(
-            'Specified subscription type "{}" not found in document.'.format(
-                subscription_type_name
-            )
-        )
-
-    inner_type_map = OrderedDict(
-        [
-            ("String", GraphQLString),
-            ("Int", GraphQLInt),
-            ("Float", GraphQLFloat),
-            ("Boolean", GraphQLBoolean),
-            ("ID", GraphQLID),
-            ("__Schema", __Schema),
-            ("__Directive", __Directive),
-            ("__DirectiveLocation", __DirectiveLocation),
-            ("__Type", __Type),
-            ("__Field", __Field),
-            ("__InputValue", __InputValue),
-            ("__EnumValue", __EnumValue),
-            ("__TypeKind", __TypeKind),
-        ]
-    )
-
-    def get_directive(directive_ast):
-        return GraphQLDirective(
-            name=directive_ast.name.value,
-            locations=[node.value for node in directive_ast.locations],
-            args=make_input_values(directive_ast.arguments, GraphQLArgument),
-        )
-
-    def get_object_type(type_ast):
-        type = type_def_named(type_ast.name.value)
-        assert isinstance(type, GraphQLObjectType), "AST must provide object type"
-        return type
-
-    def produce_type_def(type_ast):
-        type_name = _get_named_type_ast(type_ast).name.value
-        type_def = type_def_named(type_name)
-        return _build_wrapped_type(type_def, type_ast)
-
-    def type_def_named(type_name):
-        if type_name in inner_type_map:
-            return inner_type_map[type_name]
-
-        if type_name not in ast_map:
-            raise Exception('Type "{}" not found in document'.format(type_name))
-
-        inner_type_def = make_schema_def(ast_map[type_name])
-        if not inner_type_def:
-            raise Exception('Nothing constructed for "{}".'.format(type_name))
-
-        inner_type_map[type_name] = inner_type_def
-        return inner_type_def
-
-    def make_schema_def(definition):
-        if not definition:
-            raise Exception("def must be defined.")
-
-        handler = _schema_def_handlers.get(type(definition))
-        if not handler:
-            raise Exception(
-                'Type kind "{}" not supported.'.format(type(definition).__name__)
-            )
-
-        return handler(definition)
-
-    def make_type_def(definition):
-        return GraphQLObjectType(
-            name=definition.name.value,
-            fields=lambda: make_field_def_map(definition),
-            interfaces=make_implemented_interfaces(definition),
-        )
-
-    def make_field_def_map(definition):
-        return OrderedDict(
-            (
-                f.name.value,
-                GraphQLField(
-                    type=produce_type_def(f.type),
-                    args=make_input_values(f.arguments, GraphQLArgument),
-                    deprecation_reason=get_deprecation_reason(f.directives),
-                ),
-            )
-            for f in definition.fields
-        )
-
-    def make_implemented_interfaces(definition):
-        return [produce_type_def(i) for i in definition.interfaces]
-
-    def make_input_values(values, cls):
-        return OrderedDict(
-            (
-                value.name.value,
-                cls(
-                    type=produce_type_def(value.type),
-                    default_value=value_from_ast(
-                        value.default_value, produce_type_def(value.type)
-                    ),
-                ),
-            )
-            for value in values
-        )
-
-    def make_interface_def(definition):
-        return GraphQLInterfaceType(
-            name=definition.name.value,
-            resolve_type=_none,
-            fields=lambda: make_field_def_map(definition),
-        )
-
-    def make_enum_def(definition):
-        values = OrderedDict(
-            (
-                v.name.value,
-                GraphQLEnumValue(
-                    deprecation_reason=get_deprecation_reason(v.directives)
-                ),
-            )
-            for v in definition.values
-        )
-        return GraphQLEnumType(name=definition.name.value, values=values)
-
-    def make_union_def(definition):
-        return GraphQLUnionType(
-            name=definition.name.value,
-            resolve_type=_none,
-            types=[produce_type_def(t) for t in definition.types],
-        )
-
-    def make_scalar_def(definition):
-        return GraphQLScalarType(
-            name=definition.name.value,
-            serialize=_none,
-            # Validation calls the parse functions to determine if a literal value is correct.
-            # Returning none, however would cause the scalar to fail validation. Returning false,
-            # will cause them to pass.
-            parse_literal=_false,
-            parse_value=_false,
-        )
-
-    def make_input_object_def(definition):
-        return GraphQLInputObjectType(
-            name=definition.name.value,
-            fields=lambda: make_input_values(
-                definition.fields, GraphQLInputObjectField
-            ),
-        )
-
-    _schema_def_handlers = {
-        ast.ObjectTypeDefinition: make_type_def,
-        ast.InterfaceTypeDefinition: make_interface_def,
-        ast.EnumTypeDefinition: make_enum_def,
-        ast.UnionTypeDefinition: make_union_def,
-        ast.ScalarTypeDefinition: make_scalar_def,
-        ast.InputObjectTypeDefinition: make_input_object_def,
-    }
-    types = [type_def_named(definition.name.value) for definition in type_defs]
-    directives = [get_directive(d) for d in directive_defs]
-
-    # If specified directive were not explicitly declared, add them.
-    find_skip_directive = (
-        directive.name for directive in directives if directive.name == "skip"
-    )
-    find_include_directive = (
-        directive.name for directive in directives if directive.name == "include"
-    )
-    find_deprecated_directive = (
-        directive.name for directive in directives if directive.name == "deprecated"
-    )
-
-    if not next(find_skip_directive, None):
-        directives.append(GraphQLSkipDirective)
-
-    if not next(find_include_directive, None):
-        directives.append(GraphQLIncludeDirective)
-
-    if not next(find_deprecated_directive, None):
-        directives.append(GraphQLDeprecatedDirective)
-
-    schema_kwargs = {"query": get_object_type(ast_map[query_type_name])}
-
-    if mutation_type_name:
-        schema_kwargs["mutation"] = get_object_type(ast_map[mutation_type_name])
-
-    if subscription_type_name:
-        schema_kwargs["subscription"] = get_object_type(ast_map[subscription_type_name])
-
-    if directive_defs:
-        schema_kwargs["directives"] = directives
-
-    if types:
-        schema_kwargs["types"] = types
-
-    return GraphQLSchema(**schema_kwargs)
-
-
-def get_deprecation_reason(directives):
-    deprecated_ast = next(
-        (
-            directive
-            for directive in directives
-            if directive.name.value == GraphQLDeprecatedDirective.name
-        ),
-        None,
-    )
-
-    if deprecated_ast:
-        args = get_argument_values(
-            GraphQLDeprecatedDirective.args, deprecated_ast.arguments
-        )
-        return args["reason"]
-    else:
-        return None
diff --git a/graphql/utils/build_client_schema.py b/graphql/utils/build_client_schema.py
deleted file mode 100644
index 2ae23aa..0000000
--- a/graphql/utils/build_client_schema.py
+++ /dev/null
@@ -1,316 +0,0 @@
-from ..language.parser import parse_value
-from ..pyutils.ordereddict import OrderedDict
-from ..type import (
-    GraphQLArgument,
-    GraphQLBoolean,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLFloat,
-    GraphQLID,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInt,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-    is_input_type,
-    is_output_type,
-)
-from ..type.directives import DirectiveLocation, GraphQLDirective
-from ..type.introspection import (
-    TypeKind,
-    __Directive,
-    __DirectiveLocation,
-    __EnumValue,
-    __Field,
-    __InputValue,
-    __Schema,
-    __Type,
-    __TypeKind,
-)
-from .value_from_ast import value_from_ast
-
-
-def _false(*_):
-    return False
-
-
-def _none(*_):
-    return None
-
-
-def no_execution(root, info, **args):
-    raise Exception("Client Schema cannot be used for execution.")
-
-
-def build_client_schema(introspection):
-    schema_introspection = introspection["__schema"]
-
-    type_introspection_map = {t["name"]: t for t in schema_introspection["types"]}
-
-    type_def_cache = {
-        "String": GraphQLString,
-        "Int": GraphQLInt,
-        "Float": GraphQLFloat,
-        "Boolean": GraphQLBoolean,
-        "ID": GraphQLID,
-        "__Schema": __Schema,
-        "__Directive": __Directive,
-        "__DirectiveLocation": __DirectiveLocation,
-        "__Type": __Type,
-        "__Field": __Field,
-        "__InputValue": __InputValue,
-        "__EnumValue": __EnumValue,
-        "__TypeKind": __TypeKind,
-    }
-
-    def get_type(type_ref):
-        kind = type_ref.get("kind")
-
-        if kind == TypeKind.LIST:
-            item_ref = type_ref.get("ofType")
-
-            if not item_ref:
-                raise Exception("Decorated type deeper than introspection query.")
-
-            return GraphQLList(get_type(item_ref))
-
-        elif kind == TypeKind.NON_NULL:
-            nullable_ref = type_ref.get("ofType")
-            if not nullable_ref:
-                raise Exception("Decorated type deeper than introspection query.")
-
-            return GraphQLNonNull(get_type(nullable_ref))
-
-        return get_named_type(type_ref["name"])
-
-    def get_named_type(type_name):
-        if type_name in type_def_cache:
-            return type_def_cache[type_name]
-
-        type_introspection = type_introspection_map.get(type_name)
-        if not type_introspection:
-            raise Exception(
-                "Invalid or incomplete schema, unknown type: {}. Ensure that a full introspection query "
-                "is used in order to build a client schema.".format(type_name)
-            )
-
-        type_def = type_def_cache[type_name] = build_type(type_introspection)
-        return type_def
-
-    def get_input_type(type_ref):
-        input_type = get_type(type_ref)
-        assert is_input_type(
-            input_type
-        ), "Introspection must provide input type for arguments."
-        return input_type
-
-    def get_output_type(type_ref):
-        output_type = get_type(type_ref)
-        assert is_output_type(
-            output_type
-        ), "Introspection must provide output type for fields."
-        return output_type
-
-    def get_object_type(type_ref):
-        object_type = get_type(type_ref)
-        assert isinstance(
-            object_type, GraphQLObjectType
-        ), "Introspection must provide object type for possibleTypes."
-        return object_type
-
-    def get_interface_type(type_ref):
-        interface_type = get_type(type_ref)
-        assert isinstance(
-            interface_type, GraphQLInterfaceType
-        ), "Introspection must provide interface type for interfaces."
-        return interface_type
-
-    def build_type(type):
-        type_kind = type.get("kind")
-        handler = type_builders.get(type_kind)
-        if not handler:
-            raise Exception(
-                "Invalid or incomplete schema, unknown kind: {}. Ensure that a full introspection query "
-                "is used in order to build a client schema.".format(type_kind)
-            )
-
-        return handler(type)
-
-    def build_scalar_def(scalar_introspection):
-        return GraphQLScalarType(
-            name=scalar_introspection["name"],
-            description=scalar_introspection.get("description"),
-            serialize=_none,
-            parse_value=_false,
-            parse_literal=_false,
-        )
-
-    def build_object_def(object_introspection):
-        return GraphQLObjectType(
-            name=object_introspection["name"],
-            description=object_introspection.get("description"),
-            interfaces=[
-                get_interface_type(i)
-                for i in object_introspection.get("interfaces", [])
-            ],
-            fields=lambda: build_field_def_map(object_introspection),
-        )
-
-    def build_interface_def(interface_introspection):
-        return GraphQLInterfaceType(
-            name=interface_introspection["name"],
-            description=interface_introspection.get("description"),
-            fields=lambda: build_field_def_map(interface_introspection),
-            resolve_type=no_execution,
-        )
-
-    def build_union_def(union_introspection):
-        return GraphQLUnionType(
-            name=union_introspection["name"],
-            description=union_introspection.get("description"),
-            types=[
-                get_object_type(t) for t in union_introspection.get("possibleTypes", [])
-            ],
-            resolve_type=no_execution,
-        )
-
-    def build_enum_def(enum_introspection):
-        return GraphQLEnumType(
-            name=enum_introspection["name"],
-            description=enum_introspection.get("description"),
-            values=OrderedDict(
-                [
-                    (
-                        value_introspection["name"],
-                        GraphQLEnumValue(
-                            description=value_introspection.get("description"),
-                            deprecation_reason=value_introspection.get(
-                                "deprecationReason"
-                            ),
-                        ),
-                    )
-                    for value_introspection in enum_introspection.get("enumValues", [])
-                ]
-            ),
-        )
-
-    def build_input_object_def(input_object_introspection):
-        return GraphQLInputObjectType(
-            name=input_object_introspection["name"],
-            description=input_object_introspection.get("description"),
-            fields=lambda: build_input_value_def_map(
-                input_object_introspection.get("inputFields"), GraphQLInputObjectField
-            ),
-        )
-
-    type_builders = {
-        TypeKind.SCALAR: build_scalar_def,
-        TypeKind.OBJECT: build_object_def,
-        TypeKind.INTERFACE: build_interface_def,
-        TypeKind.UNION: build_union_def,
-        TypeKind.ENUM: build_enum_def,
-        TypeKind.INPUT_OBJECT: build_input_object_def,
-    }
-
-    def build_field_def_map(type_introspection):
-        return OrderedDict(
-            [
-                (
-                    f["name"],
-                    GraphQLField(
-                        type=get_output_type(f["type"]),
-                        description=f.get("description"),
-                        resolver=no_execution,
-                        deprecation_reason=f.get("deprecationReason"),
-                        args=build_input_value_def_map(f.get("args"), GraphQLArgument),
-                    ),
-                )
-                for f in type_introspection.get("fields", [])
-            ]
-        )
-
-    def build_default_value(f):
-        default_value = f.get("defaultValue")
-        if default_value is None:
-            return None
-
-        return value_from_ast(parse_value(default_value), get_input_type(f["type"]))
-
-    def build_input_value_def_map(input_value_introspection, argument_type):
-        return OrderedDict(
-            [
-                (f["name"], build_input_value(f, argument_type))
-                for f in input_value_introspection
-            ]
-        )
-
-    def build_input_value(input_value_introspection, argument_type):
-        input_value = argument_type(
-            description=input_value_introspection["description"],
-            type=get_input_type(input_value_introspection["type"]),
-            default_value=build_default_value(input_value_introspection),
-        )
-        return input_value
-
-    def build_directive(directive_introspection):
-        # Support deprecated `on****` fields for building `locations`, as this
-        # is used by GraphiQL which may need to support outdated servers.
-        locations = list(directive_introspection.get("locations", []))
-        if not locations:
-            locations = []
-            if directive_introspection.get("onField", False):
-                locations += list(DirectiveLocation.FIELD_LOCATIONS)
-            if directive_introspection.get("onOperation", False):
-                locations += list(DirectiveLocation.OPERATION_LOCATIONS)
-            if directive_introspection.get("onFragment", False):
-                locations += list(DirectiveLocation.FRAGMENT_LOCATIONS)
-
-        return GraphQLDirective(
-            name=directive_introspection["name"],
-            description=directive_introspection.get("description"),
-            # TODO: {} ?
-            args=build_input_value_def_map(
-                directive_introspection.get("args", {}), GraphQLArgument
-            ),
-            locations=locations,
-        )
-
-    # Iterate through all types, getting the type definition for each, ensuring
-    # that any type not directly referenced by a field will get created.
-    types = [
-        get_named_type(type_introspection_name)
-        for type_introspection_name in type_introspection_map.keys()
-    ]
-
-    query_type = get_object_type(schema_introspection["queryType"])
-    mutation_type = (
-        get_object_type(schema_introspection["mutationType"])
-        if schema_introspection.get("mutationType")
-        else None
-    )
-    subscription_type = (
-        get_object_type(schema_introspection["subscriptionType"])
-        if schema_introspection.get("subscriptionType")
-        else None
-    )
-
-    directives = (
-        [build_directive(d) for d in schema_introspection["directives"]]
-        if schema_introspection["directives"]
-        else []
-    )
-
-    return GraphQLSchema(
-        query=query_type,
-        mutation=mutation_type,
-        subscription=subscription_type,
-        directives=directives,
-        types=types,
-    )
diff --git a/graphql/utils/concat_ast.py b/graphql/utils/concat_ast.py
deleted file mode 100644
index 1f5cfdc..0000000
--- a/graphql/utils/concat_ast.py
+++ /dev/null
@@ -1,16 +0,0 @@
-import itertools
-
-from ..language.ast import Document
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Iterable
-
-
-def concat_ast(asts):
-    # type: (Iterable[Document]) -> Document
-    return Document(
-        definitions=list(
-            itertools.chain.from_iterable(document.definitions for document in asts)
-        )
-    )
diff --git a/graphql/utils/extend_schema.py b/graphql/utils/extend_schema.py
deleted file mode 100644
index 7f4868d..0000000
--- a/graphql/utils/extend_schema.py
+++ /dev/null
@@ -1,389 +0,0 @@
-from collections import defaultdict
-
-from ..error import GraphQLError
-from ..language import ast
-from ..pyutils.ordereddict import OrderedDict
-from ..type.definition import (
-    GraphQLArgument,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLUnionType,
-)
-from ..type.introspection import (
-    __Directive,
-    __DirectiveLocation,
-    __EnumValue,
-    __Field,
-    __InputValue,
-    __Schema,
-    __Type,
-    __TypeKind,
-)
-from ..type.scalars import (
-    GraphQLBoolean,
-    GraphQLFloat,
-    GraphQLID,
-    GraphQLInt,
-    GraphQLString,
-)
-from ..type.schema import GraphQLSchema
-from .value_from_ast import value_from_ast
-
-
-def extend_schema(schema, documentAST=None):
-    """Produces a new schema given an existing schema and a document which may
-    contain GraphQL type extensions and definitions. The original schema will
-    remain unaltered.
-
-    Because a schema represents a graph of references, a schema cannot be
-    extended without effectively making an entire copy. We do not know until it's
-    too late if subgraphs remain unchanged.
-
-    This algorithm copies the provided schema, applying extensions while
-    producing the copy. The original schema remains unaltered."""
-
-    assert isinstance(schema, GraphQLSchema), "Must provide valid GraphQLSchema"
-    assert documentAST and isinstance(
-        documentAST, ast.Document
-    ), "Must provide valid Document AST"
-
-    # Collect the type definitions and extensions found in the document.
-    type_definition_map = {}
-    type_extensions_map = defaultdict(list)
-
-    for _def in documentAST.definitions:
-        if isinstance(
-            _def,
-            (
-                ast.ObjectTypeDefinition,
-                ast.InterfaceTypeDefinition,
-                ast.EnumTypeDefinition,
-                ast.UnionTypeDefinition,
-                ast.ScalarTypeDefinition,
-                ast.InputObjectTypeDefinition,
-            ),
-        ):
-            # Sanity check that none of the defined types conflict with the
-            # schema's existing types.
-            type_name = _def.name.value
-            if schema.get_type(type_name):
-                raise GraphQLError(
-                    (
-                        'Type "{}" already exists in the schema. It cannot also '
-                        + "be defined in this type definition."
-                    ).format(type_name),
-                    [_def],
-                )
-
-            type_definition_map[type_name] = _def
-        elif isinstance(_def, ast.TypeExtensionDefinition):
-            # Sanity check that this type extension exists within the
-            # schema's existing types.
-            extended_type_name = _def.definition.name.value
-            existing_type = schema.get_type(extended_type_name)
-            if not existing_type:
-                raise GraphQLError(
-                    (
-                        'Cannot extend type "{}" because it does not '
-                        + "exist in the existing schema."
-                    ).format(extended_type_name),
-                    [_def.definition],
-                )
-            if not isinstance(existing_type, GraphQLObjectType):
-                raise GraphQLError(
-                    'Cannot extend non-object type "{}".'.format(extended_type_name),
-                    [_def.definition],
-                )
-
-            type_extensions_map[extended_type_name].append(_def)
-
-    # Below are functions used for producing this schema that have closed over
-    # this scope and have access to the schema, cache, and newly defined types.
-
-    def get_type_from_def(type_def):
-        type = _get_named_type(type_def.name)
-        assert type, "Invalid schema"
-        return type
-
-    def get_type_from_AST(astNode):
-        type = _get_named_type(astNode.name.value)
-        if not type:
-            raise GraphQLError(
-                (
-                    'Unknown type: "{}". Ensure that this type exists '
-                    + "either in the original schema, or is added in a type definition."
-                ).format(astNode.name.value),
-                [astNode],
-            )
-        return type
-
-    # Given a name, returns a type from either the existing schema or an
-    # added type.
-    def _get_named_type(typeName):
-        cached_type_def = type_def_cache.get(typeName)
-        if cached_type_def:
-            return cached_type_def
-
-        existing_type = schema.get_type(typeName)
-        if existing_type:
-            type_def = extend_type(existing_type)
-            type_def_cache[typeName] = type_def
-            return type_def
-
-        type_ast = type_definition_map.get(typeName)
-        if type_ast:
-            type_def = build_type(type_ast)
-            type_def_cache[typeName] = type_def
-            return type_def
-
-    # Given a type's introspection result, construct the correct
-    # GraphQLType instance.
-    def extend_type(type):
-        if isinstance(type, GraphQLObjectType):
-            return extend_object_type(type)
-        if isinstance(type, GraphQLInterfaceType):
-            return extend_interface_type(type)
-        if isinstance(type, GraphQLUnionType):
-            return extend_union_type(type)
-        return type
-
-    def extend_object_type(type):
-        return GraphQLObjectType(
-            name=type.name,
-            description=type.description,
-            interfaces=lambda: extend_implemented_interfaces(type),
-            fields=lambda: extend_field_map(type),
-        )
-
-    def extend_interface_type(type):
-        return GraphQLInterfaceType(
-            name=type.name,
-            description=type.description,
-            fields=lambda: extend_field_map(type),
-            resolve_type=cannot_execute_client_schema,
-        )
-
-    def extend_union_type(type):
-        return GraphQLUnionType(
-            name=type.name,
-            description=type.description,
-            types=list(map(get_type_from_def, type.types)),
-            resolve_type=cannot_execute_client_schema,
-        )
-
-    def extend_implemented_interfaces(type):
-        interfaces = list(map(get_type_from_def, type.interfaces))
-
-        # If there are any extensions to the interfaces, apply those here.
-        extensions = type_extensions_map[type.name]
-        for extension in extensions:
-            for namedType in extension.definition.interfaces:
-                interface_name = namedType.name.value
-                if any([_def.name == interface_name for _def in interfaces]):
-                    raise GraphQLError(
-                        (
-                            'Type "{}" already implements "{}". '
-                            + "It cannot also be implemented in this type extension."
-                        ).format(type.name, interface_name),
-                        [namedType],
-                    )
-                interfaces.append(get_type_from_AST(namedType))
-
-        return interfaces
-
-    def extend_field_map(type):
-        new_field_map = OrderedDict()
-        old_field_map = type.fields
-        for field_name, field in old_field_map.items():
-            new_field_map[field_name] = GraphQLField(
-                extend_field_type(field.type),
-                description=field.description,
-                deprecation_reason=field.deprecation_reason,
-                args=field.args,
-                resolver=cannot_execute_client_schema,
-            )
-
-        # If there are any extensions to the fields, apply those here.
-        extensions = type_extensions_map[type.name]
-        for extension in extensions:
-            for field in extension.definition.fields:
-                field_name = field.name.value
-                if field_name in old_field_map:
-                    raise GraphQLError(
-                        (
-                            'Field "{}.{}" already exists in the '
-                            + "schema. It cannot also be defined in this type extension."
-                        ).format(type.name, field_name),
-                        [field],
-                    )
-                new_field_map[field_name] = GraphQLField(
-                    build_field_type(field.type),
-                    args=build_input_values(field.arguments),
-                    resolver=cannot_execute_client_schema,
-                )
-
-        return new_field_map
-
-    def extend_field_type(type):
-        if isinstance(type, GraphQLList):
-            return GraphQLList(extend_field_type(type.of_type))
-        if isinstance(type, GraphQLNonNull):
-            return GraphQLNonNull(extend_field_type(type.of_type))
-        return get_type_from_def(type)
-
-    def build_type(type_ast):
-        _type_build = {
-            ast.ObjectTypeDefinition: build_object_type,
-            ast.InterfaceTypeDefinition: build_interface_type,
-            ast.UnionTypeDefinition: build_union_type,
-            ast.ScalarTypeDefinition: build_scalar_type,
-            ast.EnumTypeDefinition: build_enum_type,
-            ast.InputObjectTypeDefinition: build_input_object_type,
-        }
-        func = _type_build.get(type(type_ast))
-        if func:
-            return func(type_ast)
-
-    def build_object_type(type_ast):
-        return GraphQLObjectType(
-            type_ast.name.value,
-            interfaces=lambda: build_implemented_interfaces(type_ast),
-            fields=lambda: build_field_map(type_ast),
-        )
-
-    def build_interface_type(type_ast):
-        return GraphQLInterfaceType(
-            type_ast.name.value,
-            fields=lambda: build_field_map(type_ast),
-            resolve_type=cannot_execute_client_schema,
-        )
-
-    def build_union_type(type_ast):
-        return GraphQLUnionType(
-            type_ast.name.value,
-            types=list(map(get_type_from_AST, type_ast.types)),
-            resolve_type=cannot_execute_client_schema,
-        )
-
-    def build_scalar_type(type_ast):
-        return GraphQLScalarType(
-            type_ast.name.value,
-            serialize=lambda *args, **kwargs: None,
-            # Note: validation calls the parse functions to determine if a
-            # literal value is correct. Returning null would cause use of custom
-            # scalars to always fail validation. Returning false causes them to
-            # always pass validation.
-            parse_value=lambda *args, **kwargs: False,
-            parse_literal=lambda *args, **kwargs: False,
-        )
-
-    def build_enum_type(type_ast):
-        return GraphQLEnumType(
-            type_ast.name.value,
-            values={v.name.value: GraphQLEnumValue() for v in type_ast.values},
-        )
-
-    def build_input_object_type(type_ast):
-        return GraphQLInputObjectType(
-            type_ast.name.value,
-            fields=lambda: build_input_values(type_ast.fields, GraphQLInputObjectField),
-        )
-
-    def build_implemented_interfaces(type_ast):
-        return list(map(get_type_from_AST, type_ast.interfaces))
-
-    def build_field_map(type_ast):
-        return {
-            field.name.value: GraphQLField(
-                build_field_type(field.type),
-                args=build_input_values(field.arguments),
-                resolver=cannot_execute_client_schema,
-            )
-            for field in type_ast.fields
-        }
-
-    def build_input_values(values, input_type=GraphQLArgument):
-        input_values = OrderedDict()
-        for value in values:
-            type = build_field_type(value.type)
-            input_values[value.name.value] = input_type(
-                type, default_value=value_from_ast(value.default_value, type)
-            )
-        return input_values
-
-    def build_field_type(type_ast):
-        if isinstance(type_ast, ast.ListType):
-            return GraphQLList(build_field_type(type_ast.type))
-        if isinstance(type_ast, ast.NonNullType):
-            return GraphQLNonNull(build_field_type(type_ast.type))
-        return get_type_from_AST(type_ast)
-
-    # If this document contains no new types, then return the same unmodified
-    # GraphQLSchema instance.
-    if not type_extensions_map and not type_definition_map:
-        return schema
-
-    # A cache to use to store the actual GraphQLType definition objects by name.
-    # Initialize to the GraphQL built in scalars and introspection types. All
-    # functions below are inline so that this type def cache is within the scope
-    # of the closure.
-
-    type_def_cache = {
-        "String": GraphQLString,
-        "Int": GraphQLInt,
-        "Float": GraphQLFloat,
-        "Boolean": GraphQLBoolean,
-        "ID": GraphQLID,
-        "__Schema": __Schema,
-        "__Directive": __Directive,
-        "__DirectiveLocation": __DirectiveLocation,
-        "__Type": __Type,
-        "__Field": __Field,
-        "__InputValue": __InputValue,
-        "__EnumValue": __EnumValue,
-        "__TypeKind": __TypeKind,
-    }
-
-    # Get the root Query, Mutation, and Subscription types.
-    query_type = get_type_from_def(schema.get_query_type())
-
-    existing_mutation_type = schema.get_mutation_type()
-    mutationType = (
-        existing_mutation_type and get_type_from_def(existing_mutation_type) or None
-    )
-
-    existing_subscription_type = schema.get_subscription_type()
-    subscription_type = (
-        existing_subscription_type
-        and get_type_from_def(existing_subscription_type)
-        or None
-    )
-
-    # Iterate through all types, getting the type definition for each, ensuring
-    # that any type not directly referenced by a field will get created.
-    types = [get_type_from_def(_def) for _def in schema.get_type_map().values()]
-
-    # Do the same with new types, appending to the list of defined types.
-    types += [get_type_from_AST(_def) for _def in type_definition_map.values()]
-
-    # Then produce and return a Schema with these types.
-    return GraphQLSchema(
-        query=query_type,
-        mutation=mutationType,
-        subscription=subscription_type,
-        # Copy directives.
-        directives=schema.get_directives(),
-        types=types,
-    )
-
-
-def cannot_execute_client_schema(*args, **kwargs):
-    raise Exception("Client Schema cannot be used for execution.")
diff --git a/graphql/utils/get_field_def.py b/graphql/utils/get_field_def.py
deleted file mode 100644
index 6743ff3..0000000
--- a/graphql/utils/get_field_def.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from ..type.definition import GraphQLInterfaceType, GraphQLObjectType, GraphQLUnionType
-from ..type.introspection import (
-    SchemaMetaFieldDef,
-    TypeMetaFieldDef,
-    TypeNameMetaFieldDef,
-)
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.ast import Field
-    from ..type.definition import GraphQLField
-    from ..type.schema import GraphQLSchema
-    from typing import Optional, Union
-
-
-def get_field_def(
-    schema,  # type: GraphQLSchema
-    parent_type,  # type: Union[GraphQLInterfaceType, GraphQLObjectType]
-    field_ast,  # type: Field
-):
-    # type: (...) -> Optional[GraphQLField]
-    """Not exactly the same as the executor's definition of get_field_def, in this
-    statically evaluated environment we do not always have an Object type,
-    and need to handle Interface and Union types."""
-    name = field_ast.name.value
-    if name == "__schema" and schema.get_query_type() == parent_type:
-        return SchemaMetaFieldDef
-
-    elif name == "__type" and schema.get_query_type() == parent_type:
-        return TypeMetaFieldDef
-
-    elif name == "__typename" and isinstance(
-        parent_type, (GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType)
-    ):
-        return TypeNameMetaFieldDef
-
-    elif isinstance(parent_type, (GraphQLObjectType, GraphQLInterfaceType)):
-        return parent_type.fields.get(name)
-
-    return None
diff --git a/graphql/utils/get_operation_ast.py b/graphql/utils/get_operation_ast.py
deleted file mode 100644
index cd5ba26..0000000
--- a/graphql/utils/get_operation_ast.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from ..language import ast
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.ast import Document, OperationDefinition
-    from typing import Optional
-
-
-def get_operation_ast(document_ast, operation_name=None):
-    # type: (Document, Optional[str]) -> Optional[OperationDefinition]
-    operation = None
-
-    for definition in document_ast.definitions:
-        if isinstance(definition, ast.OperationDefinition):
-            if not operation_name:
-                # If no operation name is provided, only return an Operation if it is the only one present in the
-                # document. This means that if we've encountered a second operation as we were iterating over the
-                # definitions in the document, there are more than one Operation defined, and we should return None.
-                if operation:
-                    return None
-
-                operation = definition
-
-            elif definition.name and definition.name.value == operation_name:
-                return definition
-
-    return operation
diff --git a/graphql/utils/introspection_query.py b/graphql/utils/introspection_query.py
deleted file mode 100644
index f8b8293..0000000
--- a/graphql/utils/introspection_query.py
+++ /dev/null
@@ -1,90 +0,0 @@
-introspection_query = """
-  query IntrospectionQuery {
-    __schema {
-      queryType { name }
-      mutationType { name }
-      subscriptionType { name }
-      types {
-        ...FullType
-      }
-      directives {
-        name
-        description
-        locations
-        args {
-          ...InputValue
-        }
-      }
-    }
-  }
-  fragment FullType on __Type {
-    kind
-    name
-    description
-    fields(includeDeprecated: true) {
-      name
-      description
-      args {
-        ...InputValue
-      }
-      type {
-        ...TypeRef
-      }
-      isDeprecated
-      deprecationReason
-    }
-    inputFields {
-      ...InputValue
-    }
-    interfaces {
-      ...TypeRef
-    }
-    enumValues(includeDeprecated: true) {
-      name
-      description
-      isDeprecated
-      deprecationReason
-    }
-    possibleTypes {
-      ...TypeRef
-    }
-  }
-  fragment InputValue on __InputValue {
-    name
-    description
-    type { ...TypeRef }
-    defaultValue
-  }
-  fragment TypeRef on __Type {
-    kind
-    name
-    ofType {
-      kind
-      name
-      ofType {
-        kind
-        name
-        ofType {
-          kind
-          name
-          ofType {
-            kind
-            name
-            ofType {
-              kind
-              name
-              ofType {
-                kind
-                name
-                ofType {
-                  kind
-                  name
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-"""
diff --git a/graphql/utils/is_valid_literal_value.py b/graphql/utils/is_valid_literal_value.py
deleted file mode 100644
index 1b65d87..0000000
--- a/graphql/utils/is_valid_literal_value.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from ..language import ast
-from ..language.printer import print_ast
-from ..type.definition import (
-    GraphQLEnumType,
-    GraphQLInputObjectType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLScalarType,
-)
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.ast import ObjectValue, StringValue
-    from typing import Union, Any, List
-
-_empty_list = []  # type: List
-
-
-def is_valid_literal_value(type, value_ast):
-    # type: (Union[GraphQLInputObjectType, GraphQLScalarType, GraphQLNonNull, GraphQLList], Any) -> List
-    if isinstance(type, GraphQLNonNull):
-        of_type = type.of_type
-        if not value_ast:
-            return [u'Expected "{}", found null.'.format(type)]
-
-        return is_valid_literal_value(of_type, value_ast)  # type: ignore
-
-    if not value_ast:
-        return _empty_list
-
-    if isinstance(value_ast, ast.Variable):
-        return _empty_list
-
-    if isinstance(type, GraphQLList):
-        item_type = type.of_type
-        if isinstance(value_ast, ast.ListValue):
-            errors = []
-
-            for i, item_ast in enumerate(value_ast.values):
-                item_errors = is_valid_literal_value(item_type, item_ast)
-                for error in item_errors:
-                    errors.append(u"In element #{}: {}".format(i, error))
-
-            return errors
-
-        return is_valid_literal_value(item_type, value_ast)
-
-    if isinstance(type, GraphQLInputObjectType):
-        if not isinstance(value_ast, ast.ObjectValue):
-            return [u'Expected "{}", found not an object.'.format(type)]
-
-        fields = type.fields
-        field_asts = value_ast.fields
-
-        errors = []
-        for provided_field_ast in field_asts:
-            if provided_field_ast.name.value not in fields:
-                errors.append(
-                    u'In field "{}": Unknown field.'.format(
-                        provided_field_ast.name.value
-                    )
-                )
-
-        field_ast_map = {field_ast.name.value: field_ast for field_ast in field_asts}
-
-        def get_field_ast_value(field_name):
-            # type: (str) -> Union[None, ObjectValue, StringValue]
-            if field_name in field_ast_map:
-                return field_ast_map[field_name].value
-            return None
-
-        for field_name, field in fields.items():
-            subfield_errors = is_valid_literal_value(
-                field.type, get_field_ast_value(field_name)
-            )
-            errors.extend(
-                u'In field "{}": {}'.format(field_name, e) for e in subfield_errors
-            )
-
-        return errors
-
-    assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), "Must be input type"
-
-    parse_result = type.parse_literal(value_ast)
-    if parse_result is None:
-        return [
-            u'Expected type "{}", found {}.'.format(type.name, print_ast(value_ast))
-        ]
-
-    return _empty_list
diff --git a/graphql/utils/is_valid_value.py b/graphql/utils/is_valid_value.py
deleted file mode 100644
index 179c45d..0000000
--- a/graphql/utils/is_valid_value.py
+++ /dev/null
@@ -1,82 +0,0 @@
-"""
-    Implementation of isValidJSValue from graphql.s
-"""
-
-try:
-    from collections.abc import Iterable, Mapping
-except ImportError:  # Python < 3.3
-    from collections import Iterable, Mapping
-import json
-
-from six import string_types
-
-from ..type import (
-    GraphQLEnumType,
-    GraphQLInputObjectType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLScalarType,
-)
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import Any, List
-
-_empty_list = []  # type: List
-
-
-def is_valid_value(value, type):
-    # type: (Any, Any) -> List
-    """Given a type and any value, return True if that value is valid."""
-    if isinstance(type, GraphQLNonNull):
-        of_type = type.of_type
-        if value is None:
-            return [u'Expected "{}", found null.'.format(type)]
-
-        return is_valid_value(value, of_type)
-
-    if value is None:
-        return _empty_list
-
-    if isinstance(type, GraphQLList):
-        item_type = type.of_type
-        if not isinstance(value, string_types) and isinstance(value, Iterable):
-            errors = []
-            for i, item in enumerate(value):
-                item_errors = is_valid_value(item, item_type)
-                for error in item_errors:
-                    errors.append(u"In element #{}: {}".format(i, error))
-
-            return errors
-
-        else:
-            return is_valid_value(value, item_type)
-
-    if isinstance(type, GraphQLInputObjectType):
-        if not isinstance(value, Mapping):
-            return [u'Expected "{}", found not an object.'.format(type)]
-
-        fields = type.fields
-        errors = []
-
-        for provided_field in sorted(value.keys()):
-            if provided_field not in fields:
-                errors.append(u'In field "{}": Unknown field.'.format(provided_field))
-
-        for field_name, field in fields.items():
-            subfield_errors = is_valid_value(value.get(field_name), field.type)
-            errors.extend(
-                u'In field "{}": {}'.format(field_name, e) for e in subfield_errors
-            )
-
-        return errors
-
-    assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), "Must be input type"
-
-    # Scalar/Enum input checks to ensure the type can parse the value to
-    # a non-null value.
-    parse_result = type.parse_value(value)
-    if parse_result is None:
-        return [u'Expected type "{}", found {}.'.format(type, json.dumps(value))]
-
-    return _empty_list
diff --git a/graphql/utils/quoted_or_list.py b/graphql/utils/quoted_or_list.py
deleted file mode 100644
index 8931cec..0000000
--- a/graphql/utils/quoted_or_list.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import functools
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import List
-
-
-MAX_LENGTH = 5
-
-
-def quoted_or_list(items):
-    # type: (List[str]) -> str
-    """Given [ A, B, C ] return '"A", "B" or "C"'."""
-    selected = items[:MAX_LENGTH]
-    quoted_items = ('"{}"'.format(t) for t in selected)
-
-    def quoted_or_text(text, quoted_and_index):
-        index = quoted_and_index[0]
-        quoted_item = quoted_and_index[1]
-        text += (
-            (", " if len(selected) > 2 and not index == len(selected) - 1 else " ")
-            + ("or " if index == len(selected) - 1 else "")
-            + quoted_item
-        )
-        return text
-
-    enumerated_items = enumerate(quoted_items)
-    first_item = next(enumerated_items)[1]
-    return functools.reduce(quoted_or_text, enumerated_items, first_item)
diff --git a/graphql/utils/schema_printer.py b/graphql/utils/schema_printer.py
deleted file mode 100644
index f319333..0000000
--- a/graphql/utils/schema_printer.py
+++ /dev/null
@@ -1,219 +0,0 @@
-from ..language.printer import print_ast
-from ..type.definition import (
-    GraphQLEnumType,
-    GraphQLInputObjectType,
-    GraphQLInterfaceType,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLUnionType,
-)
-from ..type.directives import DEFAULT_DEPRECATION_REASON
-from .ast_from_value import ast_from_value
-
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..type.definition import (
-        GraphQLArgument,
-        GraphQLType,
-        GraphQLField,
-        GraphQLEnumValue,
-    )
-    from ..type.schema import GraphQLSchema
-    from ..type.directives import GraphQLDirective
-    from typing import Any, Union, Callable
-
-
-def print_schema(schema):
-    # type: (GraphQLSchema) -> str
-    return _print_filtered_schema(
-        schema, lambda n: not (is_spec_directive(n)), _is_defined_type
-    )
-
-
-def print_introspection_schema(schema):
-    # type: (GraphQLSchema) -> str
-    return _print_filtered_schema(schema, is_spec_directive, _is_introspection_type)
-
-
-def is_spec_directive(directive_name):
-    # type: (str) -> bool
-    return directive_name in ("skip", "include", "deprecated")
-
-
-def _is_defined_type(typename):
-    # type: (Any) -> bool
-    return not _is_introspection_type(typename) and not _is_builtin_scalar(typename)
-
-
-def _is_introspection_type(typename):
-    # type: (str) -> bool
-    return typename.startswith("__")
-
-
-_builtin_scalars = frozenset(["String", "Boolean", "Int", "Float", "ID"])
-
-
-def _is_builtin_scalar(typename):
-    # type: (str) -> bool
-    return typename in _builtin_scalars
-
-
-def _print_filtered_schema(schema, directive_filter, type_filter):
-    # type: (GraphQLSchema, Callable[[str], bool], Callable[[str], bool]) -> str
-    return (
-        "\n\n".join(
-            [_print_schema_definition(schema)]
-            + [
-                _print_directive(directive)
-                for directive in schema.get_directives()
-                if directive_filter(directive.name)
-            ]
-            + [
-                _print_type(type)
-                for typename, type in sorted(schema.get_type_map().items())
-                if type_filter(typename)
-            ]
-        )
-        + "\n"
-    )
-
-
-def _print_schema_definition(schema):
-    # type: (GraphQLSchema) -> str
-    operation_types = []
-
-    query_type = schema.get_query_type()
-    if query_type:
-        operation_types.append("  query: {}".format(query_type))
-
-    mutation_type = schema.get_mutation_type()
-    if mutation_type:
-        operation_types.append("  mutation: {}".format(mutation_type))
-
-    subscription_type = schema.get_subscription_type()
-    if subscription_type:
-        operation_types.append("  subscription: {}".format(subscription_type))
-
-    return "schema {{\n{}\n}}".format("\n".join(operation_types))
-
-
-def _print_type(type):
-    # type: (GraphQLType) -> str
-    if isinstance(type, GraphQLScalarType):
-        return _print_scalar(type)
-
-    elif isinstance(type, GraphQLObjectType):
-        return _print_object(type)
-
-    elif isinstance(type, GraphQLInterfaceType):
-        return _print_interface(type)
-
-    elif isinstance(type, GraphQLUnionType):
-        return _print_union(type)
-
-    elif isinstance(type, GraphQLEnumType):
-        return _print_enum(type)
-
-    assert isinstance(type, GraphQLInputObjectType)
-    return _print_input_object(type)
-
-
-def _print_scalar(type):
-    # type: (GraphQLScalarType) -> str
-    return "scalar {}".format(type.name)
-
-
-def _print_object(type):
-    # type: (GraphQLObjectType) -> str
-    interfaces = type.interfaces
-    implemented_interfaces = (
-        " implements {}".format(", ".join(i.name for i in interfaces))
-        if interfaces
-        else ""
-    )
-
-    return ("type {}{} {{\n" "{}\n" "}}").format(
-        type.name, implemented_interfaces, _print_fields(type)
-    )
-
-
-def _print_interface(type):
-    # type: (GraphQLInterfaceType) -> str
-    return ("interface {} {{\n" "{}\n" "}}").format(type.name, _print_fields(type))
-
-
-def _print_union(type):
-    # type: (GraphQLUnionType) -> str
-    return "union {} = {}".format(type.name, " | ".join(str(t) for t in type.types))
-
-
-def _print_enum(type):
-    # type: (GraphQLEnumType) -> str
-    return ("enum {} {{\n" "{}\n" "}}").format(
-        type.name, "\n".join("  " + v.name + _print_deprecated(v) for v in type.values)
-    )
-
-
-def _print_input_object(type):
-    # type: (GraphQLInputObjectType) -> str
-    return ("input {} {{\n" "{}\n" "}}").format(
-        type.name,
-        "\n".join(
-            "  " + _print_input_value(name, field)
-            for name, field in type.fields.items()
-        ),
-    )
-
-
-def _print_fields(type):
-    # type: (Union[GraphQLObjectType, GraphQLInterfaceType]) -> str
-    return "\n".join(
-        "  {}{}: {}{}".format(f_name, _print_args(f), f.type, _print_deprecated(f))
-        for f_name, f in type.fields.items()
-    )
-
-
-def _print_deprecated(field_or_enum_value):
-    # type: (Union[GraphQLField, GraphQLEnumValue]) -> str
-    reason = field_or_enum_value.deprecation_reason
-
-    if reason is None:
-        return ""
-    elif reason in ("", DEFAULT_DEPRECATION_REASON):
-        return " @deprecated"
-    else:
-        return " @deprecated(reason: {})".format(print_ast(ast_from_value(reason)))
-
-
-def _print_args(field_or_directives):
-    # type: (Union[GraphQLField, GraphQLDirective]) -> str
-    if not field_or_directives.args:
-        return ""
-
-    return "({})".format(
-        ", ".join(
-            _print_input_value(arg_name, arg)
-            for arg_name, arg in field_or_directives.args.items()
-        )
-    )
-
-
-def _print_input_value(name, arg):
-    # type: (str, GraphQLArgument) -> str
-    if arg.default_value is not None:
-        default_value = " = " + print_ast(ast_from_value(arg.default_value, arg.type))
-    else:
-        default_value = ""
-
-    return "{}: {}{}".format(name, arg.type, default_value)
-
-
-def _print_directive(directive):
-    # type: (GraphQLDirective) -> str
-    return "directive @{}{} on {}".format(
-        directive.name, _print_args(directive), " | ".join(directive.locations)
-    )
-
-
-__all__ = ["print_schema", "print_introspection_schema"]
diff --git a/graphql/utils/suggestion_list.py b/graphql/utils/suggestion_list.py
deleted file mode 100644
index fc5ca55..0000000
--- a/graphql/utils/suggestion_list.py
+++ /dev/null
@@ -1,52 +0,0 @@
-from collections import OrderedDict
-
-
-def suggestion_list(inp, options):
-    """
-     Given an invalid input string and a list of valid options, returns a filtered
-     list of valid options sorted based on their similarity with the input.
-    """
-    options_by_distance = OrderedDict()
-    input_threshold = len(inp) / 2
-
-    for option in options:
-        distance = lexical_distance(inp, option)
-        threshold = max(input_threshold, len(option) / 2, 1)
-        if distance <= threshold:
-            options_by_distance[option] = distance
-
-    return sorted(
-        list(options_by_distance.keys()), key=lambda k: options_by_distance[k]
-    )
-
-
-def lexical_distance(a, b):
-    """
-     Computes the lexical distance between strings A and B.
-     The "distance" between two strings is given by counting the minimum number
-     of edits needed to transform string A into string B. An edit can be an
-     insertion, deletion, or substitution of a single character, or a swap of two
-     adjacent characters.
-     This distance can be useful for detecting typos in input or sorting
-     @returns distance in number of edits
-    """
-
-    d = [[i] for i in range(len(a) + 1)] or []
-    d_len = len(d) or 1
-    for i in range(d_len):
-        for j in range(1, len(b) + 1):
-            if i == 0:
-                d[i].append(j)
-            else:
-                d[i].append(0)
-
-    for i in range(1, len(a) + 1):
-        for j in range(1, len(b) + 1):
-            cost = 0 if a[i - 1] == b[j - 1] else 1
-
-            d[i][j] = min(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost)
-
-            if i > 1 and j < 1 and a[i - 1] == b[j - 2] and a[i - 2] == b[j - 1]:
-                d[i][j] = min(d[i][j], d[i - 2][j - 2] + cost)
-
-    return d[len(a)][len(b)]
diff --git a/graphql/utils/tests/__init__.py b/graphql/utils/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/utils/tests/test_ast_from_value.py b/graphql/utils/tests/test_ast_from_value.py
deleted file mode 100644
index b25f1ed..0000000
--- a/graphql/utils/tests/test_ast_from_value.py
+++ /dev/null
@@ -1,89 +0,0 @@
-from collections import OrderedDict
-
-from graphql.language import ast
-from graphql.type.definition import (
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLList,
-)
-from graphql.type.scalars import GraphQLFloat
-from graphql.utils.ast_from_value import ast_from_value
-
-
-def test_converts_boolean_values_to_asts():
-    assert ast_from_value(True) == ast.BooleanValue(True)
-    assert ast_from_value(False) == ast.BooleanValue(False)
-
-
-def test_converts_numeric_values_to_asts():
-    assert ast_from_value(123) == ast.IntValue("123")
-    assert ast_from_value(123.0) == ast.IntValue("123")
-    assert ast_from_value(123.5) == ast.FloatValue("123.5")
-    assert ast_from_value(1e4) == ast.IntValue("10000")
-    assert ast_from_value(1e40) == ast.FloatValue("1e+40")
-
-
-def test_it_converts_numeric_values_to_float_asts():
-    assert ast_from_value(123, GraphQLFloat) == ast.FloatValue("123.0")
-    assert ast_from_value(123.0, GraphQLFloat) == ast.FloatValue("123.0")
-    assert ast_from_value(123.5, GraphQLFloat) == ast.FloatValue("123.5")
-    assert ast_from_value(1e4, GraphQLFloat) == ast.FloatValue("10000.0")
-    assert ast_from_value(1e40, GraphQLFloat) == ast.FloatValue("1e+40")
-
-
-def test_it_converts_string_values_to_asts():
-    assert ast_from_value("hello") == ast.StringValue("hello")
-    assert ast_from_value("VALUE") == ast.StringValue("VALUE")
-    assert ast_from_value(u"VAL\nUE") == ast.StringValue("VAL\\nUE")
-    assert ast_from_value("VAL\nUE") == ast.StringValue("VAL\\nUE")
-    assert ast_from_value("123") == ast.StringValue("123")
-
-
-my_enum = GraphQLEnumType(
-    "MyEnum", {"HELLO": GraphQLEnumValue(1), "GOODBYE": GraphQLEnumValue(2)}
-)
-
-
-def test_converts_string_values_to_enum_asts_if_possible():
-    assert ast_from_value("hello", my_enum) == ast.EnumValue("hello")
-    assert ast_from_value("HELLO", my_enum) == ast.EnumValue("HELLO")
-    assert ast_from_value("VAL\nUE", my_enum) == ast.StringValue("VAL\\nUE")
-    assert ast_from_value("123", my_enum) == ast.StringValue("123")
-
-
-def test_converts_array_values_to_list_asts():
-    assert ast_from_value(["FOO", "BAR"]) == ast.ListValue(
-        values=[ast.StringValue("FOO"), ast.StringValue("BAR")]
-    )
-
-
-def test_converts_list_singletons():
-    assert ast_from_value("FOO", GraphQLList(my_enum)) == ast.EnumValue("FOO")
-
-
-def test_converts_input_objects():
-    value = OrderedDict([("foo", 3), ("bar", "HELLO")])
-
-    assert ast_from_value(value) == ast.ObjectValue(
-        fields=[
-            ast.ObjectField(name=ast.Name("foo"), value=ast.IntValue("3")),
-            ast.ObjectField(name=ast.Name("bar"), value=ast.StringValue("HELLO")),
-        ]
-    )
-
-    input_obj = GraphQLInputObjectType(
-        "MyInputObj",
-        {
-            "foo": GraphQLInputObjectField(GraphQLFloat),
-            "bar": GraphQLInputObjectField(my_enum),
-        },
-    )
-
-    assert ast_from_value(value, input_obj) == ast.ObjectValue(
-        fields=[
-            ast.ObjectField(name=ast.Name("foo"), value=ast.FloatValue("3.0")),
-            ast.ObjectField(name=ast.Name("bar"), value=ast.EnumValue("HELLO")),
-        ]
-    )
diff --git a/graphql/utils/tests/test_ast_to_code.py b/graphql/utils/tests/test_ast_to_code.py
deleted file mode 100644
index 29d62d4..0000000
--- a/graphql/utils/tests/test_ast_to_code.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from graphql import Source, parse
-from graphql.language import ast
-from graphql.language.parser import Loc
-from graphql.utils.ast_to_code import ast_to_code
-
-from ...language.tests import fixtures
-
-
-def test_ast_to_code_using_kitchen_sink():
-    doc = parse(fixtures.KITCHEN_SINK)
-    code_ast = ast_to_code(doc)
-    source = Source(fixtures.KITCHEN_SINK)
-
-    def loc(start, end):
-        return Loc(start, end, source)
-
-    parsed_code_ast = eval(code_ast, {}, {"ast": ast, "loc": loc})
-    assert doc == parsed_code_ast
diff --git a/graphql/utils/tests/test_ast_to_dict.py b/graphql/utils/tests/test_ast_to_dict.py
deleted file mode 100644
index cc48096..0000000
--- a/graphql/utils/tests/test_ast_to_dict.py
+++ /dev/null
@@ -1,137 +0,0 @@
-from graphql.language import ast
-from graphql.language.parser import Loc, parse
-from graphql.utils.ast_to_dict import ast_to_dict
-
-
-def test_converts_simple_ast_to_dict():
-    node = ast.Name(value="test", loc=Loc(start=5, end=10))
-
-    assert ast_to_dict(node) == {"kind": "Name", "value": "test"}
-    assert ast_to_dict(node, include_loc=True) == {
-        "kind": "Name",
-        "value": "test",
-        "loc": {"start": 5, "end": 10},
-    }
-
-
-def test_converts_nested_ast_to_dict():
-    parsed_ast = parse(
-        """
-        query x {
-            someQuery(arg: "x") {
-                a
-                b
-            }
-            fragment Test on TestFoo {
-                c
-                d
-            }
-        }
-    """
-    )
-
-    expected_ast_dict = {
-        "definitions": [
-            {
-                "directives": [],
-                "kind": "OperationDefinition",
-                "name": {"kind": "Name", "value": "x"},
-                "operation": "query",
-                "selection_set": {
-                    "kind": "SelectionSet",
-                    "selections": [
-                        {
-                            "alias": None,
-                            "arguments": [
-                                {
-                                    "kind": "Argument",
-                                    "name": {"kind": "Name", "value": "arg"},
-                                    "value": {"kind": "StringValue", "value": "x"},
-                                }
-                            ],
-                            "directives": [],
-                            "kind": "Field",
-                            "name": {"kind": "Name", "value": "someQuery"},
-                            "selection_set": {
-                                "kind": "SelectionSet",
-                                "selections": [
-                                    {
-                                        "alias": None,
-                                        "arguments": [],
-                                        "directives": [],
-                                        "kind": "Field",
-                                        "name": {"kind": "Name", "value": "a"},
-                                        "selection_set": None,
-                                    },
-                                    {
-                                        "alias": None,
-                                        "arguments": [],
-                                        "directives": [],
-                                        "kind": "Field",
-                                        "name": {"kind": "Name", "value": "b"},
-                                        "selection_set": None,
-                                    },
-                                ],
-                            },
-                        },
-                        {
-                            "alias": None,
-                            "arguments": [],
-                            "directives": [],
-                            "kind": "Field",
-                            "name": {"kind": "Name", "value": "fragment"},
-                            "selection_set": None,
-                        },
-                        {
-                            "alias": None,
-                            "arguments": [],
-                            "directives": [],
-                            "kind": "Field",
-                            "name": {"kind": "Name", "value": "Test"},
-                            "selection_set": None,
-                        },
-                        {
-                            "alias": None,
-                            "arguments": [],
-                            "directives": [],
-                            "kind": "Field",
-                            "name": {"kind": "Name", "value": "on"},
-                            "selection_set": None,
-                        },
-                        {
-                            "alias": None,
-                            "arguments": [],
-                            "directives": [],
-                            "kind": "Field",
-                            "name": {"kind": "Name", "value": "TestFoo"},
-                            "selection_set": {
-                                "kind": "SelectionSet",
-                                "selections": [
-                                    {
-                                        "alias": None,
-                                        "arguments": [],
-                                        "directives": [],
-                                        "kind": "Field",
-                                        "name": {"kind": "Name", "value": "c"},
-                                        "selection_set": None,
-                                    },
-                                    {
-                                        "alias": None,
-                                        "arguments": [],
-                                        "directives": [],
-                                        "kind": "Field",
-                                        "name": {"kind": "Name", "value": "d"},
-                                        "selection_set": None,
-                                    },
-                                ],
-                            },
-                        },
-                    ],
-                },
-                "variable_definitions": [],
-            }
-        ],
-        "kind": "Document",
-    }
-
-    assert ast_to_dict(parsed_ast) == expected_ast_dict
diff --git a/graphql/utils/tests/test_build_ast_schema.py b/graphql/utils/tests/test_build_ast_schema.py
deleted file mode 100644
index 6f84aa6..0000000
--- a/graphql/utils/tests/test_build_ast_schema.py
+++ /dev/null
@@ -1,804 +0,0 @@
-from pytest import raises
-
-from graphql import GraphQLInt, parse
-from graphql.utils.build_ast_schema import build_ast_schema
-from graphql.utils.schema_printer import print_schema
-
-from ...type import (
-    GraphQLDeprecatedDirective,
-    GraphQLIncludeDirective,
-    GraphQLSkipDirective,
-)
-
-
-def cycle_output(body):
-    """This function does a full cycle of going from a string with the contents of the DSL,
-    parsed in a schema AST, materializing that schema AST into an in-memory GraphQLSchema,
-    and then finally printing that GraphQL into the DSL"""
-    ast = parse(body)
-    schema = build_ast_schema(ast)
-    return "\n" + print_schema(schema)
-
-
-def test_simple_type():
-    body = """
-schema {
-  query: HelloScalars
-}
-
-type HelloScalars {
-  str: String
-  int: Int
-  float: Float
-  id: ID
-  bool: Boolean
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_with_directives():
-    body = """
-schema {
-  query: Hello
-}
-
-directive @foo(arg: Int) on FIELD
-
-type Hello {
-  str: String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_maintains_skip_and_include_directives():
-    body = """
-    schema {
-        query: Hello
-    }
-
-    type Hello {
-        str: String
-    }
-    """
-
-    schema = build_ast_schema(parse(body))
-    assert len(schema.get_directives()) == 3
-    assert schema.get_directive("skip") == GraphQLSkipDirective
-    assert schema.get_directive("include") == GraphQLIncludeDirective
-    assert schema.get_directive("deprecated") == GraphQLDeprecatedDirective
-
-
-def test_overriding_directives_excludes_specified():
-    body = """
-    schema {
-        query: Hello
-    }
-
-    directive @skip on FIELD
-    directive @include on FIELD
-    directive @deprecated on FIELD_DEFINITION
-
-    type Hello {
-        str: String
-    }
-    """
-
-    schema = build_ast_schema(parse(body))
-    assert len(schema.get_directives()) == 3
-    assert schema.get_directive("skip") != GraphQLSkipDirective
-    assert schema.get_directive("skip") is not None
-    assert schema.get_directive("include") != GraphQLIncludeDirective
-    assert schema.get_directive("include") is not None
-    assert schema.get_directive("deprecated") != GraphQLDeprecatedDirective
-    assert schema.get_directive("deprecated") is not None
-
-
-def test_overriding_skip_directive_excludes_built_in_one():
-    body = """
-    schema {
-        query: Hello
-    }
-
-    directive @skip on FIELD
-
-    type Hello {
-        str: String
-    }
-    """
-
-    schema = build_ast_schema(parse(body))
-    assert len(schema.get_directives()) == 3
-    assert schema.get_directive("skip") != GraphQLSkipDirective
-    assert schema.get_directive("skip") is not None
-    assert schema.get_directive("include") == GraphQLIncludeDirective
-    assert schema.get_directive("deprecated") == GraphQLDeprecatedDirective
-
-
-def test_overriding_include_directive_excludes_built_in_one():
-    body = """
-    schema {
-        query: Hello
-    }
-
-    directive @include on FIELD
-
-    type Hello {
-        str: String
-    }
-    """
-
-    schema = build_ast_schema(parse(body))
-    assert len(schema.get_directives()) == 3
-    assert schema.get_directive("skip") == GraphQLSkipDirective
-    assert schema.get_directive("deprecated") == GraphQLDeprecatedDirective
-    assert schema.get_directive("include") != GraphQLIncludeDirective
-    assert schema.get_directive("include") is not None
-
-
-def test_adding_directives_maintains_skip_and_include_directives():
-    body = """
-    schema {
-        query: Hello
-    }
-
-    directive @foo(arg: Int) on FIELD
-
-    type Hello {
-        str: String
-    }
-    """
-
-    schema = build_ast_schema(parse(body))
-    assert len(schema.get_directives()) == 4
-    assert schema.get_directive("skip") == GraphQLSkipDirective
-    assert schema.get_directive("include") == GraphQLIncludeDirective
-    assert schema.get_directive("deprecated") == GraphQLDeprecatedDirective
-
-
-def test_type_modifiers():
-    body = """
-schema {
-  query: HelloScalars
-}
-
-type HelloScalars {
-  nonNullStr: String!
-  listOfStrs: [String]
-  listOfNonNullStrs: [String!]
-  nonNullListOfStrs: [String]!
-  nonNullListOfNonNullStrs: [String!]!
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_recursive_type():
-    body = """
-schema {
-  query: Recurse
-}
-
-type Recurse {
-  str: String
-  recurse: Recurse
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_two_types_circular():
-    body = """
-schema {
-  query: TypeOne
-}
-
-type TypeOne {
-  str: String
-  typeTwo: TypeTwo
-}
-
-type TypeTwo {
-  str: String
-  typeOne: TypeOne
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_single_argument_field():
-    body = """
-schema {
-  query: Hello
-}
-
-type Hello {
-  str(int: Int): String
-  floatToStr(float: Float): String
-  idToStr(id: ID): String
-  booleanToStr(bool: Boolean): String
-  strToStr(bool: String): String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_simple_type_with_multiple_arguments():
-    body = """
-schema {
-  query: Hello
-}
-
-type Hello {
-  str(int: Int, bool: Boolean): String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_simple_type_with_interface():
-    body = """
-schema {
-  query: HelloInterface
-}
-
-type HelloInterface implements WorldInterface {
-  str: String
-}
-
-interface WorldInterface {
-  str: String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_simple_output_enum():
-    body = """
-schema {
-  query: OutputEnumRoot
-}
-
-enum Hello {
-  WORLD
-}
-
-type OutputEnumRoot {
-  hello: Hello
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_simple_input_enum():
-    body = """
-schema {
-  query: InputEnumRoot
-}
-
-enum Hello {
-  WORLD
-}
-
-type InputEnumRoot {
-  str(hello: Hello): String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_multiple_value_enum():
-    body = """
-schema {
-  query: OutputEnumRoot
-}
-
-enum Hello {
-  WO
-  RLD
-}
-
-type OutputEnumRoot {
-  hello: Hello
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_simple_union():
-    body = """
-schema {
-  query: Root
-}
-
-union Hello = World
-
-type Root {
-  hello: Hello
-}
-
-type World {
-  str: String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_multiple_union():
-    body = """
-schema {
-  query: Root
-}
-
-union Hello = WorldOne | WorldTwo
-
-type Root {
-  hello: Hello
-}
-
-type WorldOne {
-  str: String
-}
-
-type WorldTwo {
-  str: String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_custom_scalar():
-    body = """
-schema {
-  query: Root
-}
-
-scalar CustomScalar
-
-type Root {
-  customScalar: CustomScalar
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_input_object():
-    body = """
-schema {
-  query: Root
-}
-
-input Input {
-  int: Int
-}
-
-type Root {
-  field(in: Input): String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_input_types_are_read():
-    schema = build_ast_schema(
-        parse(
-            """
-        schema {
-            query: Query
-        }
-
-        type Query {
-            field(input: Input): Int
-        }
-
-        input Input {
-            id: Int
-        }
-    """
-        )
-    )
-
-    input_type = schema.get_type("Input")
-    assert input_type.fields["id"].type == GraphQLInt
-
-
-def test_input_types_can_be_recursive():
-    schema = build_ast_schema(
-        parse(
-            """
-        schema {
-            query: Query
-        }
-
-        type Query {
-            field(input: Input): Int
-        }
-
-        input Input {
-            id: Input
-        }
-    """
-        )
-    )
-
-    input_type = schema.get_type("Input")
-    assert input_type.fields["id"].type == input_type
-
-
-def test_simple_argument_field_with_default():
-    body = """
-schema {
-  query: Hello
-}
-
-type Hello {
-  str(int: Int = 2): String
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_simple_type_with_mutation():
-    body = """
-schema {
-  query: HelloScalars
-  mutation: Mutation
-}
-
-type HelloScalars {
-  str: String
-  int: Int
-  bool: Boolean
-}
-
-type Mutation {
-  addHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_simple_type_with_subscription():
-    body = """
-schema {
-  query: HelloScalars
-  subscription: Subscription
-}
-
-type HelloScalars {
-  str: String
-  int: Int
-  bool: Boolean
-}
-
-type Subscription {
-  subscribeHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_unreferenced_type_implementing_referenced_interface():
-    body = """
-schema {
-  query: Query
-}
-
-type Concrete implements Iface {
-  key: String
-}
-
-interface Iface {
-  key: String
-}
-
-type Query {
-  iface: Iface
-}
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_unreferenced_type_implementing_referenced_union():
-    body = """
-schema {
-  query: Query
-}
-
-type Concrete {
-  key: String
-}
-
-type Query {
-  union: Union
-}
-
-union Union = Concrete
-"""
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_supports_deprecated_directive():
-    body = """
-schema {
-  query: Query
-}
-
-enum MyEnum {
-  VALUE
-  OLD_VALUE @deprecated
-  OTHER_VALUE @deprecated(reason: "Terrible reasons")
-}
-
-type Query {
-  field1: String @deprecated
-  field2: Int @deprecated(reason: "Because I said so")
-  enum: MyEnum
-}
-"""
-
-    output = cycle_output(body)
-    assert output == body
-
-
-def test_requires_a_schema_definition():
-    body = """
-type Hello {
-  bar: Bar
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert "Must provide a schema definition." == str(excinfo.value)
-
-
-def test_allows_only_a_single_schema_definition():
-    body = """
-schema {
-  query: Hello
-}
-
-schema {
-  query: Hello
-}
-
-type Hello {
-  bar: Bar
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert "Must provide only one schema definition." == str(excinfo.value)
-
-
-def test_requires_a_query_type():
-    body = """
-schema {
-  mutation: Hello
-}
-
-type Hello {
-  bar: Bar
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert "Must provide schema definition with query type." == str(excinfo.value)
-
-
-def test_allows_only_a_single_query_type():
-    body = """
-schema {
-  query: Hello
-  query: Yellow
-}
-
-type Hello {
-  bar: Bar
-}
-
-type Yellow {
-  isColor: Boolean
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert "Must provide only one query type in schema." == str(excinfo.value)
-
-
-def test_allows_only_a_single_mutation_type():
-    body = """
-schema {
-  query: Hello
-  mutation: Hello
-  mutation: Yellow
-}
-
-type Hello {
-  bar: Bar
-}
-
-type Yellow {
-  isColor: Boolean
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert "Must provide only one mutation type in schema." == str(excinfo.value)
-
-
-def test_allows_only_a_single_subscription_type():
-    body = """
-schema {
-  query: Hello
-  subscription: Hello
-  subscription: Yellow
-}
-
-type Hello {
-  bar: Bar
-}
-
-type Yellow {
-  isColor: Boolean
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert "Must provide only one subscription type in schema." == str(excinfo.value)
-
-
-def test_unknown_type_referenced():
-    body = """
-schema {
-  query: Hello
-}
-
-type Hello {
-  bar: Bar
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert 'Type "Bar" not found in document' in str(excinfo.value)
-
-
-def test_unknown_type_in_union_list():
-    body = """
-schema {
-  query: Hello
-}
-
-union TestUnion = Bar
-type Hello { testUnion: TestUnion }
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert 'Type "Bar" not found in document' in str(excinfo.value)
-
-
-def test_unknown_query_type():
-    body = """
-schema {
-  query: Wat
-}
-
-type Hello {
-  str: String
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert 'Specified query type "Wat" not found in document' in str(excinfo.value)
-
-
-def test_unknown_mutation_type():
-    body = """
-schema {
-  query: Hello
-  mutation: Wat
-}
-
-type Hello {
-  str: String
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert 'Specified mutation type "Wat" not found in document' in str(excinfo.value)
-
-
-def test_unknown_subscription_type():
-    body = """
-schema {
-  query: Hello
-  mutation: Wat
-  subscription: Awesome
-}
-
-type Hello {
-  str: String
-}
-
-type Wat {
-  str: String
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert 'Specified subscription type "Awesome" not found in document' in str(
-        excinfo.value
-    )
-
-
-def test_does_not_consider_query_names():
-    body = """
-schema {
-  query: Foo
-}
-
-type Hello {
-  str: String
-}
-"""
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert 'Specified query type "Foo" not found in document' in str(excinfo.value)
-
-
-def test_does_not_consider_fragment_names():
-    body = """schema {
-  query: Foo
-}
-
-fragment Foo on Type { field } """
-    doc = parse(body)
-    with raises(Exception) as excinfo:
-        build_ast_schema(doc)
-
-    assert 'Specified query type "Foo" not found in document' in str(excinfo.value)
diff --git a/graphql/utils/tests/test_build_client_schema.py b/graphql/utils/tests/test_build_client_schema.py
deleted file mode 100644
index 5e2f496..0000000
--- a/graphql/utils/tests/test_build_client_schema.py
+++ /dev/null
@@ -1,805 +0,0 @@
-from collections import OrderedDict
-
-from pytest import raises
-
-from graphql import graphql
-from graphql.error import format_error
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLBoolean,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLFloat,
-    GraphQLID,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInt,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-)
-from graphql.type.directives import GraphQLDirective
-from graphql.utils.build_client_schema import build_client_schema
-from graphql.utils.introspection_query import introspection_query
-
-from ...pyutils.contain_subset import contain_subset
-
-
-def _test_schema(server_schema):
-    initial_introspection = graphql(server_schema, introspection_query)
-    client_schema = build_client_schema(initial_introspection.data)
-    second_introspection = graphql(client_schema, introspection_query)
-    assert contain_subset(initial_introspection.data, second_introspection.data)
-
-    return client_schema
-
-
-def test_it_builds_a_simple_schema():
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Simple",
-            description="This is a simple type",
-            fields={
-                "string": GraphQLField(
-                    GraphQLString, description="This is a string field"
-                )
-            },
-        )
-    )
-    _test_schema(schema)
-
-
-def test_builds_a_simple_schema_with_both_operation_types():
-    QueryType = GraphQLObjectType(
-        name="QueryType",
-        description="This is a simple query type",
-        fields={
-            "string": GraphQLField(GraphQLString, description="This is a string field.")
-        },
-    )
-    MutationType = GraphQLObjectType(
-        name="MutationType",
-        description="This is a simple mutation type",
-        fields={
-            "setString": GraphQLField(
-                GraphQLString,
-                description="Set the string field",
-                args={"value": GraphQLArgument(GraphQLString)},
-            )
-        },
-    )
-    SubscriptionType = GraphQLObjectType(
-        name="SubscriptionType",
-        description="This is a simple subscription type",
-        fields={
-            "string": GraphQLField(
-                type=GraphQLString, description="This is a string field"
-            )
-        },
-    )
-
-    schema = GraphQLSchema(QueryType, MutationType, SubscriptionType)
-    _test_schema(schema)
-
-
-def test_uses_built_in_scalars_when_possible():
-    customScalar = GraphQLScalarType(name="CustomScalar", serialize=lambda: None)
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Scalars",
-            fields=OrderedDict(
-                [
-                    ("int", GraphQLField(GraphQLInt)),
-                    ("float", GraphQLField(GraphQLFloat)),
-                    ("string", GraphQLField(GraphQLString)),
-                    ("boolean", GraphQLField(GraphQLBoolean)),
-                    ("id", GraphQLField(GraphQLID)),
-                    ("custom", GraphQLField(customScalar)),
-                ]
-            ),
-        )
-    )
-
-    client_schema = _test_schema(schema)
-
-    assert client_schema.get_type("Int") == GraphQLInt
-    assert client_schema.get_type("Float") == GraphQLFloat
-    assert client_schema.get_type("String") == GraphQLString
-    assert client_schema.get_type("Boolean") == GraphQLBoolean
-    assert client_schema.get_type("ID") == GraphQLID
-
-    assert client_schema.get_type("CustomScalar") != customScalar
-
-
-def test_builds_a_schema_with_a_recursive_type_reference():
-    recurType = GraphQLObjectType(
-        name="Recur", fields=lambda: {"recur": GraphQLField(recurType)}
-    )
-
-    schema = GraphQLSchema(query=recurType)
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_a_circular_type_reference():
-    DogType = GraphQLObjectType(
-        name="Dog", fields=lambda: {"bestFriend": GraphQLField(HumanType)}
-    )
-
-    HumanType = GraphQLObjectType(
-        name="Human", fields=lambda: {"bestFriend": GraphQLField(DogType)}
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Circular",
-            fields=OrderedDict(
-                [("dog", GraphQLField(DogType)), ("human", GraphQLField(HumanType))]
-            ),
-        )
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_an_interface():
-    FriendlyType = GraphQLInterfaceType(
-        name="Friendly",
-        resolve_type=lambda: None,
-        fields=lambda: {
-            "bestFriend": GraphQLField(
-                FriendlyType, description="The best friend of this friendly thing."
-            )
-        },
-    )
-
-    DogType = GraphQLObjectType(
-        name="DogType",
-        interfaces=[FriendlyType],
-        fields=lambda: {"bestFriend": GraphQLField(FriendlyType)},
-    )
-
-    HumanType = GraphQLObjectType(
-        name="Human",
-        interfaces=[FriendlyType],
-        fields=lambda: {"bestFriend": GraphQLField(FriendlyType)},
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="WithInterface", fields={"friendly": GraphQLField(FriendlyType)}
-        ),
-        types=[DogType, HumanType],
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_an_implicit_interface():
-    FriendlyType = GraphQLInterfaceType(
-        name="Friendly",
-        resolve_type=lambda: None,
-        fields=lambda: {
-            "bestFriend": GraphQLField(
-                FriendlyType, description="The best friend of this friendly thing."
-            )
-        },
-    )
-
-    DogType = GraphQLObjectType(
-        name="DogType",
-        interfaces=[FriendlyType],
-        fields=lambda: {"bestFriend": GraphQLField(DogType)},
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="WithInterface", fields={"dog": GraphQLField(DogType)}
-        )
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_a_union():
-    DogType = GraphQLObjectType(
-        name="Dog", fields=lambda: {"bestFriend": GraphQLField(FriendlyType)}
-    )
-
-    HumanType = GraphQLObjectType(
-        name="Human", fields=lambda: {"bestFriend": GraphQLField(FriendlyType)}
-    )
-
-    FriendlyType = GraphQLUnionType(
-        name="Friendly", resolve_type=lambda: None, types=[DogType, HumanType]
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="WithUnion", fields={"friendly": GraphQLField(FriendlyType)}
-        )
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_complex_field_values():
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="ComplexFields",
-            fields=OrderedDict(
-                [
-                    ("string", GraphQLField(GraphQLString)),
-                    ("listOfString", GraphQLField(GraphQLList(GraphQLString))),
-                    ("nonNullString", GraphQLField(GraphQLNonNull(GraphQLString))),
-                    (
-                        "nonNullListOfString",
-                        GraphQLField(GraphQLNonNull(GraphQLList(GraphQLString))),
-                    ),
-                    (
-                        "nonNullListOfNonNullString",
-                        GraphQLField(
-                            GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString)))
-                        ),
-                    ),
-                ]
-            ),
-        )
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_field_arguments():
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="ArgFields",
-            fields=OrderedDict(
-                [
-                    (
-                        "one",
-                        GraphQLField(
-                            GraphQLString,
-                            description="A field with a single arg",
-                            args={
-                                "intArg": GraphQLArgument(
-                                    GraphQLInt, description="This is an int arg"
-                                )
-                            },
-                        ),
-                    ),
-                    (
-                        "two",
-                        GraphQLField(
-                            GraphQLString,
-                            description="A field with two args",
-                            args=OrderedDict(
-                                [
-                                    (
-                                        "listArg",
-                                        GraphQLArgument(
-                                            GraphQLList(GraphQLInt),
-                                            description="This is a list of int arg",
-                                        ),
-                                    ),
-                                    (
-                                        "requiredArg",
-                                        GraphQLArgument(
-                                            GraphQLNonNull(GraphQLBoolean),
-                                            description="This is a required arg",
-                                        ),
-                                    ),
-                                ]
-                            ),
-                        ),
-                    ),
-                ]
-            ),
-        )
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_an_enum():
-    FoodEnum = GraphQLEnumType(
-        name="Food",
-        description="Varieties of food stuffs",
-        values=OrderedDict(
-            [
-                (
-                    "VEGETABLES",
-                    GraphQLEnumValue(1, description="Foods that are vegetables."),
-                ),
-                ("FRUITS", GraphQLEnumValue(2, description="Foods that are fruits.")),
-                ("OILS", GraphQLEnumValue(3, description="Foods that are oils.")),
-                ("DAIRY", GraphQLEnumValue(4, description="Foods that are dairy.")),
-                ("MEAT", GraphQLEnumValue(5, description="Foods that are meat.")),
-            ]
-        ),
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="EnumFields",
-            fields={
-                "food": GraphQLField(
-                    FoodEnum,
-                    description="Repeats the arg you give it",
-                    args={
-                        "kind": GraphQLArgument(
-                            FoodEnum, description="what kind of food?"
-                        )
-                    },
-                )
-            },
-        )
-    )
-
-    client_schema = _test_schema(schema)
-    clientFoodEnum = client_schema.get_type("Food")
-    assert isinstance(clientFoodEnum, GraphQLEnumType)
-
-    assert clientFoodEnum.values == [
-        GraphQLEnumValue(
-            name="VEGETABLES",
-            value="VEGETABLES",
-            description="Foods that are vegetables.",
-            deprecation_reason=None,
-        ),
-        GraphQLEnumValue(
-            name="FRUITS",
-            value="FRUITS",
-            description="Foods that are fruits.",
-            deprecation_reason=None,
-        ),
-        GraphQLEnumValue(
-            name="OILS",
-            value="OILS",
-            description="Foods that are oils.",
-            deprecation_reason=None,
-        ),
-        GraphQLEnumValue(
-            name="DAIRY",
-            value="DAIRY",
-            description="Foods that are dairy.",
-            deprecation_reason=None,
-        ),
-        GraphQLEnumValue(
-            name="MEAT",
-            value="MEAT",
-            description="Foods that are meat.",
-            deprecation_reason=None,
-        ),
-    ]
-
-
-def test_builds_a_schema_with_an_input_object():
-    AddressType = GraphQLInputObjectType(
-        name="Address",
-        description="An input address",
-        fields=OrderedDict(
-            [
-                (
-                    "street",
-                    GraphQLInputObjectField(
-                        GraphQLNonNull(GraphQLString),
-                        description="What street is this address?",
-                    ),
-                ),
-                (
-                    "city",
-                    GraphQLInputObjectField(
-                        GraphQLNonNull(GraphQLString),
-                        description="The city the address is within?",
-                    ),
-                ),
-                (
-                    "country",
-                    GraphQLInputObjectField(
-                        GraphQLString,
-                        description="The country (blank will assume USA).",
-                        default_value="USA",
-                    ),
-                ),
-            ]
-        ),
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="HasInputObjectFields",
-            fields={
-                "geocode": GraphQLField(
-                    description="Get a geocode from an address",
-                    type=GraphQLString,
-                    args={
-                        "address": GraphQLArgument(
-                            description="The address to lookup", type=AddressType
-                        )
-                    },
-                )
-            },
-        )
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_field_arguments_with_default_values():
-    GeoType = GraphQLInputObjectType(
-        name="Geo",
-        fields=OrderedDict(
-            [
-                ("lat", GraphQLInputObjectField(GraphQLFloat)),
-                ("lon", GraphQLInputObjectField(GraphQLFloat)),
-            ]
-        ),
-    )
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="ArgFields",
-            fields=OrderedDict(
-                [
-                    (
-                        "defaultInt",
-                        GraphQLField(
-                            GraphQLString,
-                            args={
-                                "intArg": GraphQLArgument(GraphQLInt, default_value=10)
-                            },
-                        ),
-                    ),
-                    (
-                        "defaultList",
-                        GraphQLField(
-                            GraphQLString,
-                            args={
-                                "listArg": GraphQLArgument(
-                                    GraphQLList(GraphQLInt), default_value=[1, 2, 3]
-                                )
-                            },
-                        ),
-                    ),
-                    (
-                        "defaultObject",
-                        GraphQLField(
-                            GraphQLString,
-                            args={
-                                "objArg": GraphQLArgument(
-                                    GeoType,
-                                    default_value={"lat": 37.485, "lon": -122.148},
-                                )
-                            },
-                        ),
-                    ),
-                ]
-            ),
-        )
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_custom_directives():
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Simple",
-            description="This is a simple type",
-            fields={
-                "string": GraphQLField(
-                    type=GraphQLString, description="This is a string field"
-                )
-            },
-        ),
-        directives=[
-            GraphQLDirective(
-                name="customDirective",
-                description="This is a custom directive",
-                locations=["FIELD"],
-            )
-        ],
-    )
-
-    _test_schema(schema)
-
-
-def test_builds_a_schema_with_legacy_directives():
-    old_introspection = {
-        "__schema": {
-            "queryType": {"name": "Simple"},
-            "types": [
-                {
-                    "name": "Simple",
-                    "kind": "OBJECT",
-                    "fields": [
-                        {"name": "simple", "args": [], "type": {"name": "Simple"}}
-                    ],
-                    "interfaces": [],
-                }
-            ],
-            "directives": [
-                {"name": "Old1", "args": [], "onField": True},
-                {"name": "Old2", "args": [], "onFragment": True},
-                {"name": "Old3", "args": [], "onOperation": True},
-                {"name": "Old4", "args": [], "onField": True, "onFragment": True},
-            ],
-        }
-    }
-
-    new_introspection = {
-        "__schema": {
-            "directives": [
-                {"name": "Old1", "args": [], "locations": ["FIELD"]},
-                {
-                    "name": "Old2",
-                    "args": [],
-                    "locations": [
-                        "FRAGMENT_DEFINITION",
-                        "FRAGMENT_SPREAD",
-                        "INLINE_FRAGMENT",
-                    ],
-                },
-                {
-                    "name": "Old3",
-                    "args": [],
-                    "locations": ["QUERY", "MUTATION", "SUBSCRIPTION"],
-                },
-                {
-                    "name": "Old4",
-                    "args": [],
-                    "locations": [
-                        "FIELD",
-                        "FRAGMENT_DEFINITION",
-                        "FRAGMENT_SPREAD",
-                        "INLINE_FRAGMENT",
-                    ],
-                },
-            ]
-        }
-    }
-
-    client_schema = build_client_schema(old_introspection)
-    second_introspection = graphql(client_schema, introspection_query).data
-
-    assert contain_subset(new_introspection, second_introspection)
-
-
-def test_builds_a_schema_aware_of_deprecation():
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Simple",
-            description="This is a simple type",
-            fields=OrderedDict(
-                [
-                    (
-                        "shinyString",
-                        GraphQLField(
-                            type=GraphQLString,
-                            description="This is a shiny string field",
-                        ),
-                    ),
-                    (
-                        "deprecatedString",
-                        GraphQLField(
-                            type=GraphQLString,
-                            description="This is a deprecated string field",
-                            deprecation_reason="Use shinyString",
-                        ),
-                    ),
-                    (
-                        "color",
-                        GraphQLField(
-                            type=GraphQLEnumType(
-                                name="Color",
-                                values=OrderedDict(
-                                    [
-                                        (
-                                            "RED",
-                                            GraphQLEnumValue(description="So rosy"),
-                                        ),
-                                        (
-                                            "GREEN",
-                                            GraphQLEnumValue(description="So grassy"),
-                                        ),
-                                        (
-                                            "BLUE",
-                                            GraphQLEnumValue(description="So calming"),
-                                        ),
-                                        (
-                                            "MAUVE",
-                                            GraphQLEnumValue(
-                                                description="So sickening",
-                                                deprecation_reason="No longer in fashion",
-                                            ),
-                                        ),
-                                    ]
-                                ),
-                            )
-                        ),
-                    ),
-                ]
-            ),
-        )
-    )
-
-    _test_schema(schema)
-
-
-def test_cannot_use_client_schema_for_general_execution():
-    customScalar = GraphQLScalarType(name="CustomScalar", serialize=lambda: None)
-
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "foo": GraphQLField(
-                    GraphQLString,
-                    args=OrderedDict(
-                        [
-                            ("custom1", GraphQLArgument(customScalar)),
-                            ("custom2", GraphQLArgument(customScalar)),
-                        ]
-                    ),
-                )
-            },
-        )
-    )
-
-    introspection = graphql(schema, introspection_query)
-    client_schema = build_client_schema(introspection.data)
-
-    class data:
-        foo = "bar"
-
-    result = graphql(
-        client_schema,
-        "query NoNo($v: CustomScalar) { foo(custom1: 123, custom2: $v) }",
-        data,
-        {"v": "baz"},
-    )
-
-    assert result.data == {"foo": None}
-    assert [format_error(e) for e in result.errors] == [
-        {
-            "locations": [{"column": 32, "line": 1}],
-            "message": "Client Schema cannot be used for execution.",
-            "path": ["foo"],
-        }
-    ]
-
-
-def test_throws_when_given_empty_types():
-    incomplete_introspection = {
-        "__schema": {"queryType": {"name": "QueryType"}, "types": []}
-    }
-
-    with raises(Exception) as excinfo:
-        build_client_schema(incomplete_introspection)
-
-    assert (
-        str(excinfo.value)
-        == "Invalid or incomplete schema, unknown type: QueryType. Ensure that a full "
-        "introspection query is used in order to build a client schema."
-    )
-
-
-def test_throws_when_missing_kind():
-    incomplete_introspection = {
-        "__schema": {
-            "queryType": {"name": "QueryType"},
-            "types": [{"name": "QueryType"}],
-        }
-    }
-
-    with raises(Exception) as excinfo:
-        build_client_schema(incomplete_introspection)
-
-    assert (
-        str(excinfo.value)
-        == "Invalid or incomplete schema, unknown kind: None. Ensure that a full "
-        "introspection query is used in order to build a client schema."
-    )
-
-
-def test_succeds_on_smaller_equals_than_7_deep_lists():
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "foo": GraphQLField(
-                    GraphQLNonNull(
-                        GraphQLList(
-                            GraphQLNonNull(
-                                GraphQLList(
-                                    GraphQLNonNull(
-                                        GraphQLList(GraphQLNonNull(GraphQLString))
-                                    )
-                                )
-                            )
-                        )
-                    )
-                )
-            },
-        )
-    )
-
-    introspection = graphql(schema, introspection_query)
-    build_client_schema(introspection.data)
-
-
-def test_fails_on_very_deep_lists():
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "foo": GraphQLField(
-                    GraphQLList(
-                        GraphQLList(
-                            GraphQLList(
-                                GraphQLList(
-                                    GraphQLList(
-                                        GraphQLList(
-                                            GraphQLList(
-                                                GraphQLList(GraphQLList(GraphQLString))
-                                            )
-                                        )
-                                    )
-                                )
-                            )
-                        )
-                    )
-                )
-            },
-        )
-    )
-
-    introspection = graphql(schema, introspection_query)
-
-    with raises(Exception) as excinfo:
-        build_client_schema(introspection.data)
-
-    assert str(excinfo.value) == "Decorated type deeper than introspection query."
-
-
-def test_fails_on_a_very_deep_non_null():
-    schema = GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query",
-            fields={
-                "foo": GraphQLField(
-                    GraphQLList(
-                        GraphQLList(
-                            GraphQLList(
-                                GraphQLList(
-                                    GraphQLList(
-                                        GraphQLList(
-                                            GraphQLList(
-                                                GraphQLList(
-                                                    GraphQLNonNull(GraphQLString)
-                                                )
-                                            )
-                                        )
-                                    )
-                                )
-                            )
-                        )
-                    )
-                )
-            },
-        )
-    )
-
-    introspection = graphql(schema, introspection_query)
-
-    with raises(Exception) as excinfo:
-        build_client_schema(introspection.data)
-
-    assert str(excinfo.value) == "Decorated type deeper than introspection query."
diff --git a/graphql/utils/tests/test_concat_ast.py b/graphql/utils/tests/test_concat_ast.py
deleted file mode 100644
index 977a2b3..0000000
--- a/graphql/utils/tests/test_concat_ast.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from graphql import Source, parse
-from graphql.language.printer import print_ast
-from graphql.utils.concat_ast import concat_ast
-
-
-def test_it_concatenates_two_acts_together():
-    source_a = Source("{ a, b, ... Frag }")
-    source_b = Source(
-        """
-        fragment Frag on T {
-            c
-        }
-    """
-    )
-
-    ast_a = parse(source_a)
-    ast_b = parse(source_b)
-    ast_c = concat_ast([ast_a, ast_b])
-
-    assert (
-        print_ast(ast_c)
-        == """{
-  a
-  b
-  ...Frag
-}
-
-fragment Frag on T {
-  c
-}
-"""
-    )
diff --git a/graphql/utils/tests/test_extend_schema.py b/graphql/utils/tests/test_extend_schema.py
deleted file mode 100644
index ac1c882..0000000
--- a/graphql/utils/tests/test_extend_schema.py
+++ /dev/null
@@ -1,847 +0,0 @@
-from collections import OrderedDict
-
-from pytest import raises
-
-from graphql import parse
-from graphql.execution import execute
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLID,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-)
-from graphql.utils.extend_schema import extend_schema
-from graphql.utils.schema_printer import print_schema
-
-# Test schema.
-SomeInterfaceType = GraphQLInterfaceType(
-    name="SomeInterface",
-    resolve_type=lambda: FooType,
-    fields=lambda: OrderedDict(
-        [
-            ("name", GraphQLField(GraphQLString)),
-            ("some", GraphQLField(SomeInterfaceType)),
-        ]
-    ),
-)
-
-
-FooType = GraphQLObjectType(
-    name="Foo",
-    interfaces=[SomeInterfaceType],
-    fields=lambda: OrderedDict(
-        [
-            ("name", GraphQLField(GraphQLString)),
-            ("some", GraphQLField(SomeInterfaceType)),
-            ("tree", GraphQLField(GraphQLNonNull(GraphQLList(FooType)))),
-        ]
-    ),
-)
-
-BarType = GraphQLObjectType(
-    name="Bar",
-    interfaces=[SomeInterfaceType],
-    fields=lambda: OrderedDict(
-        [
-            ("name", GraphQLField(GraphQLString)),
-            ("some", GraphQLField(SomeInterfaceType)),
-            ("foo", GraphQLField(FooType)),
-        ]
-    ),
-)
-
-BizType = GraphQLObjectType(
-    name="Biz", fields=lambda: OrderedDict([("fizz", GraphQLField(GraphQLString))])
-)
-
-SomeUnionType = GraphQLUnionType(
-    name="SomeUnion", resolve_type=lambda: FooType, types=[FooType, BizType]
-)
-
-SomeEnumType = GraphQLEnumType(
-    name="SomeEnum",
-    values=OrderedDict([("ONE", GraphQLEnumValue(1)), ("TWO", GraphQLEnumValue(2))]),
-)
-
-test_schema = GraphQLSchema(
-    query=GraphQLObjectType(
-        name="Query",
-        fields=lambda: OrderedDict(
-            [
-                ("foo", GraphQLField(FooType)),
-                ("someUnion", GraphQLField(SomeUnionType)),
-                ("someEnum", GraphQLField(SomeEnumType)),
-                (
-                    "someInterface",
-                    GraphQLField(
-                        SomeInterfaceType,
-                        args={"id": GraphQLArgument(GraphQLNonNull(GraphQLID))},
-                    ),
-                ),
-            ]
-        ),
-    ),
-    types=[FooType, BarType],
-)
-
-
-def test_returns_original_schema_if_no_type_definitions():
-    ast = parse("{ field }")
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema == test_schema
-
-
-def test_extends_without_altering_original_schema():
-    ast = parse(
-        """
-      extend type Query {
-        newField: String
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extend_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    assert "newField" in print_schema(extended_schema)
-    assert "newField" not in print_schema(test_schema)
-
-
-def test_cannot_be_used_for_execution():
-    ast = parse(
-        """
-      extend type Query {
-        newField: String
-      }
-    """
-    )
-    extended_schema = extend_schema(test_schema, ast)
-    clientQuery = parse("{ newField }")
-
-    result = execute(extended_schema, clientQuery, object())
-    assert result.data["newField"] is None
-    assert str(result.errors[0]) == "Client Schema cannot be used for execution."
-
-
-def test_extends_objects_by_adding_new_fields():
-    ast = parse(
-        """
-      extend type Foo {
-        newField: String
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    # print original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-}
-
-type Bar implements SomeInterface {
-  name: String
-  some: SomeInterface
-  foo: Foo
-}
-
-type Biz {
-  fizz: String
-}
-
-type Foo implements SomeInterface {
-  name: String
-  some: SomeInterface
-  tree: [Foo]!
-  newField: String
-}
-
-type Query {
-  foo: Foo
-  someUnion: SomeUnion
-  someEnum: SomeEnum
-  someInterface(id: ID!): SomeInterface
-}
-
-enum SomeEnum {
-  ONE
-  TWO
-}
-
-interface SomeInterface {
-  name: String
-  some: SomeInterface
-}
-
-union SomeUnion = Foo | Biz
-"""
-    )
-
-
-def test_extends_objects_by_adding_new_unused_types():
-    ast = parse(
-        """
-      type Unused {
-        someField: String
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    # print original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-}
-
-type Bar implements SomeInterface {
-  name: String
-  some: SomeInterface
-  foo: Foo
-}
-
-type Biz {
-  fizz: String
-}
-
-type Foo implements SomeInterface {
-  name: String
-  some: SomeInterface
-  tree: [Foo]!
-}
-
-type Query {
-  foo: Foo
-  someUnion: SomeUnion
-  someEnum: SomeEnum
-  someInterface(id: ID!): SomeInterface
-}
-
-enum SomeEnum {
-  ONE
-  TWO
-}
-
-interface SomeInterface {
-  name: String
-  some: SomeInterface
-}
-
-union SomeUnion = Foo | Biz
-
-type Unused {
-  someField: String
-}
-"""
-    )
-
-
-def test_extends_objects_by_adding_new_fields_with_arguments():
-    ast = parse(
-        """
-      extend type Foo {
-        newField(arg1: String, arg2: NewInputObj!): String
-      }
-      input NewInputObj {
-        field1: Int
-        field2: [Float]
-        field3: String!
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-}
-
-type Bar implements SomeInterface {
-  name: String
-  some: SomeInterface
-  foo: Foo
-}
-
-type Biz {
-  fizz: String
-}
-
-type Foo implements SomeInterface {
-  name: String
-  some: SomeInterface
-  tree: [Foo]!
-  newField(arg1: String, arg2: NewInputObj!): String
-}
-
-input NewInputObj {
-  field1: Int
-  field2: [Float]
-  field3: String!
-}
-
-type Query {
-  foo: Foo
-  someUnion: SomeUnion
-  someEnum: SomeEnum
-  someInterface(id: ID!): SomeInterface
-}
-
-enum SomeEnum {
-  ONE
-  TWO
-}
-
-interface SomeInterface {
-  name: String
-  some: SomeInterface
-}
-
-union SomeUnion = Foo | Biz
-"""
-    )
-
-
-def test_extends_objects_by_adding_new_fields_with_existing_types():
-    ast = parse(
-        """
-      extend type Foo {
-        newField(arg1: SomeEnum!): SomeEnum
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-}
-
-type Bar implements SomeInterface {
-  name: String
-  some: SomeInterface
-  foo: Foo
-}
-
-type Biz {
-  fizz: String
-}
-
-type Foo implements SomeInterface {
-  name: String
-  some: SomeInterface
-  tree: [Foo]!
-  newField(arg1: SomeEnum!): SomeEnum
-}
-
-type Query {
-  foo: Foo
-  someUnion: SomeUnion
-  someEnum: SomeEnum
-  someInterface(id: ID!): SomeInterface
-}
-
-enum SomeEnum {
-  ONE
-  TWO
-}
-
-interface SomeInterface {
-  name: String
-  some: SomeInterface
-}
-
-union SomeUnion = Foo | Biz
-"""
-    )
-
-
-def test_extends_objects_by_adding_implemented_interfaces():
-    ast = parse(
-        """
-      extend type Biz implements SomeInterface {
-        name: String
-        some: SomeInterface
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-}
-
-type Bar implements SomeInterface {
-  name: String
-  some: SomeInterface
-  foo: Foo
-}
-
-type Biz implements SomeInterface {
-  fizz: String
-  name: String
-  some: SomeInterface
-}
-
-type Foo implements SomeInterface {
-  name: String
-  some: SomeInterface
-  tree: [Foo]!
-}
-
-type Query {
-  foo: Foo
-  someUnion: SomeUnion
-  someEnum: SomeEnum
-  someInterface(id: ID!): SomeInterface
-}
-
-enum SomeEnum {
-  ONE
-  TWO
-}
-
-interface SomeInterface {
-  name: String
-  some: SomeInterface
-}
-
-union SomeUnion = Foo | Biz
-"""
-    )
-
-
-def test_extends_objects_by_adding_implemented_interfaces_2():
-    ast = parse(
-        """
-      extend type Foo {
-        newObject: NewObject
-        newInterface: NewInterface
-        newUnion: NewUnion
-        newScalar: NewScalar
-        newEnum: NewEnum
-        newTree: [Foo]!
-      }
-      type NewObject implements NewInterface {
-        baz: String
-      }
-      type NewOtherObject {
-        fizz: Int
-      }
-      interface NewInterface {
-        baz: String
-      }
-      union NewUnion = NewObject | NewOtherObject
-      scalar NewScalar
-      enum NewEnum {
-        OPTION_A
-        OPTION_B
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-}
-
-type Bar implements SomeInterface {
-  name: String
-  some: SomeInterface
-  foo: Foo
-}
-
-type Biz {
-  fizz: String
-}
-
-type Foo implements SomeInterface {
-  name: String
-  some: SomeInterface
-  tree: [Foo]!
-  newObject: NewObject
-  newInterface: NewInterface
-  newUnion: NewUnion
-  newScalar: NewScalar
-  newEnum: NewEnum
-  newTree: [Foo]!
-}
-
-enum NewEnum {
-  OPTION_A
-  OPTION_B
-}
-
-interface NewInterface {
-  baz: String
-}
-
-type NewObject implements NewInterface {
-  baz: String
-}
-
-type NewOtherObject {
-  fizz: Int
-}
-
-scalar NewScalar
-
-union NewUnion = NewObject | NewOtherObject
-
-type Query {
-  foo: Foo
-  someUnion: SomeUnion
-  someEnum: SomeEnum
-  someInterface(id: ID!): SomeInterface
-}
-
-enum SomeEnum {
-  ONE
-  TWO
-}
-
-interface SomeInterface {
-  name: String
-  some: SomeInterface
-}
-
-union SomeUnion = Foo | Biz
-"""
-    )
-
-
-def test_extends_objects_by_adding_implemented_new_interfaces():
-    ast = parse(
-        """
-      extend type Foo implements NewInterface {
-        baz: String
-      }
-      interface NewInterface {
-        baz: String
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-}
-
-type Bar implements SomeInterface {
-  name: String
-  some: SomeInterface
-  foo: Foo
-}
-
-type Biz {
-  fizz: String
-}
-
-type Foo implements SomeInterface, NewInterface {
-  name: String
-  some: SomeInterface
-  tree: [Foo]!
-  baz: String
-}
-
-interface NewInterface {
-  baz: String
-}
-
-type Query {
-  foo: Foo
-  someUnion: SomeUnion
-  someEnum: SomeEnum
-  someInterface(id: ID!): SomeInterface
-}
-
-enum SomeEnum {
-  ONE
-  TWO
-}
-
-interface SomeInterface {
-  name: String
-  some: SomeInterface
-}
-
-union SomeUnion = Foo | Biz
-"""
-    )
-
-
-def test_extends_objects_multiple_times():
-    ast = parse(
-        """
-      extend type Biz implements NewInterface {
-        buzz: String
-      }
-      extend type Biz implements SomeInterface {
-        name: String
-        some: SomeInterface
-        newFieldA: Int
-      }
-      extend type Biz {
-        newFieldA: Int
-        newFieldB: Float
-      }
-      interface NewInterface {
-        buzz: String
-      }
-    """
-    )
-    original_print = print_schema(test_schema)
-    extended_schema = extend_schema(test_schema, ast)
-    assert extended_schema != test_schema
-    assert print_schema(test_schema) == original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-}
-
-type Bar implements SomeInterface {
-  name: String
-  some: SomeInterface
-  foo: Foo
-}
-
-type Biz implements NewInterface, SomeInterface {
-  fizz: String
-  buzz: String
-  name: String
-  some: SomeInterface
-  newFieldA: Int
-  newFieldB: Float
-}
-
-type Foo implements SomeInterface {
-  name: String
-  some: SomeInterface
-  tree: [Foo]!
-}
-
-interface NewInterface {
-  buzz: String
-}
-
-type Query {
-  foo: Foo
-  someUnion: SomeUnion
-  someEnum: SomeEnum
-  someInterface(id: ID!): SomeInterface
-}
-
-enum SomeEnum {
-  ONE
-  TWO
-}
-
-interface SomeInterface {
-  name: String
-  some: SomeInterface
-}
-
-union SomeUnion = Foo | Biz
-"""
-    )
-
-
-def test_may_extend_mutations_and_subscriptions():
-    mutationSchema = GraphQLSchema(
-        query=GraphQLObjectType(
-            "Query", fields=lambda: {"queryField": GraphQLField(GraphQLString)}
-        ),
-        mutation=GraphQLObjectType(
-            "Mutation", fields={"mutationField": GraphQLField(GraphQLString)}
-        ),
-        subscription=GraphQLObjectType(
-            "Subscription", fields={"subscriptionField": GraphQLField(GraphQLString)}
-        ),
-    )
-
-    ast = parse(
-        """
-      extend type Query {
-        newQueryField: Int
-      }
-      extend type Mutation {
-        newMutationField: Int
-      }
-      extend type Subscription {
-        newSubscriptionField: Int
-      }
-    """
-    )
-    original_print = print_schema(mutationSchema)
-    extended_schema = extend_schema(mutationSchema, ast)
-    assert extended_schema != mutationSchema
-    assert print_schema(mutationSchema) == original_print
-    assert (
-        print_schema(extended_schema)
-        == """schema {
-  query: Query
-  mutation: Mutation
-  subscription: Subscription
-}
-
-type Mutation {
-  mutationField: String
-  newMutationField: Int
-}
-
-type Query {
-  queryField: String
-  newQueryField: Int
-}
-
-type Subscription {
-  subscriptionField: String
-  newSubscriptionField: Int
-}
-"""
-    )
-
-
-def test_does_not_allow_replacing_an_existing_type():
-    ast = parse(
-        """
-      type Bar {
-        baz: String
-      }
-    """
-    )
-    with raises(Exception) as exc_info:
-        extend_schema(test_schema, ast)
-
-    assert str(exc_info.value) == (
-        'Type "Bar" already exists in the schema. It cannot also be defined '
-        + "in this type definition."
-    )
-
-
-def test_does_not_allow_replacing_an_existing_field():
-    ast = parse(
-        """
-      extend type Bar {
-        foo: Foo
-      }
-    """
-    )
-    with raises(Exception) as exc_info:
-        extend_schema(test_schema, ast)
-
-    assert str(exc_info.value) == (
-        'Field "Bar.foo" already exists in the schema. It cannot also be '
-        + "defined in this type extension."
-    )
-
-
-def test_does_not_allow_replacing_an_existing_interface():
-    ast = parse(
-        """
-      extend type Foo implements SomeInterface {
-        otherField: String
-      }
-    """
-    )
-    with raises(Exception) as exc_info:
-        extend_schema(test_schema, ast)
-
-    assert str(exc_info.value) == (
-        'Type "Foo" already implements "SomeInterface". It cannot also be '
-        + "implemented in this type extension."
-    )
-
-
-def test_does_not_allow_referencing_an_unknown_type():
-    ast = parse(
-        """
-      extend type Bar {
-        quix: Quix
-      }
-    """
-    )
-    with raises(Exception) as exc_info:
-        extend_schema(test_schema, ast)
-
-    assert str(exc_info.value) == (
-        'Unknown type: "Quix". Ensure that this type exists either in the '
-        + "original schema, or is added in a type definition."
-    )
-
-
-def test_does_not_allow_extending_an_unknown_type():
-    ast = parse(
-        """
-      extend type UnknownType {
-        baz: String
-      }
-    """
-    )
-    with raises(Exception) as exc_info:
-        extend_schema(test_schema, ast)
-
-    assert str(exc_info.value) == (
-        'Cannot extend type "UnknownType" because it does not exist in the '
-        + "existing schema."
-    )
-
-
-def test_does_not_allow_extending_an_interface():
-    ast = parse(
-        """
-      extend type SomeInterface {
-        baz: String
-      }
-    """
-    )
-    with raises(Exception) as exc_info:
-        extend_schema(test_schema, ast)
-
-    assert str(exc_info.value) == 'Cannot extend non-object type "SomeInterface".'
-
-
-def test_does_not_allow_extending_a_scalar():
-    ast = parse(
-        """
-      extend type String {
-        baz: String
-      }
-    """
-    )
-    with raises(Exception) as exc_info:
-        extend_schema(test_schema, ast)
-
-    assert str(exc_info.value) == 'Cannot extend non-object type "String".'
diff --git a/graphql/utils/tests/test_get_operation_ast.py b/graphql/utils/tests/test_get_operation_ast.py
deleted file mode 100644
index 1839483..0000000
--- a/graphql/utils/tests/test_get_operation_ast.py
+++ /dev/null
@@ -1,62 +0,0 @@
-from graphql import parse
-from graphql.utils.get_operation_ast import get_operation_ast
-
-
-def test_gets_an_operation_from_a_simple_document():
-    doc = parse("{ field }")
-    assert get_operation_ast(doc) == doc.definitions[0]
-
-
-def test_gets_an_operation_from_a_document_with_named_mutation_operation():
-    doc = parse("mutation Test { field }")
-    assert get_operation_ast(doc) == doc.definitions[0]
-
-
-def test_gets_an_operation_from_a_document_with_named_subscription_operation():
-    doc = parse("subscription Test { field }")
-    assert get_operation_ast(doc) == doc.definitions[0]
-
-
-def test_does_not_get_missing_operation():
-    doc = parse("{ field } mutation Test { field }")
-    assert not get_operation_ast(doc)
-
-
-def test_does_not_get_ambiguous_unnamed_operation():
-    doc = parse("{ field } mutation TestM { field } subscription TestSub { field }")
-    assert not get_operation_ast(doc)
-
-
-def test_does_not_get_ambiguous_named_operation():
-    doc = parse(
-        "query TestQ { field } mutation TestM { field } subscription TestSub { field }"
-    )
-    assert not get_operation_ast(doc)
-
-
-def test_does_not_get_misnamed_operation():
-    doc = parse(
-        "query TestQ { field } mutation TestM { field } subscription TestSub { field }"
-    )
-    assert not get_operation_ast(doc, "Unknown")
-
-
-def test_gets_named_operation():
-    doc = parse(
-        "query TestQ { field } mutation TestM { field } subscription TestS { field }"
-    )
-    assert get_operation_ast(doc, "TestQ") == doc.definitions[0]
-    assert get_operation_ast(doc, "TestM") == doc.definitions[1]
-    assert get_operation_ast(doc, "TestS") == doc.definitions[2]
-
-
-def test_does_not_get_fragment():
-    doc = parse("fragment Foo on Type { field }")
-    assert not get_operation_ast(doc)
-    assert not get_operation_ast(doc, "Foo")
-
-
-def test_does_not_get_fragment_with_same_name_query():
-    doc = parse("fragment Foo on Type { field } query Foo { field }")
-    assert get_operation_ast(doc) == doc.definitions[1]
-    assert get_operation_ast(doc, "Foo") == doc.definitions[1]
diff --git a/graphql/utils/tests/test_quoted_or_list.py b/graphql/utils/tests/test_quoted_or_list.py
deleted file mode 100644
index f709f77..0000000
--- a/graphql/utils/tests/test_quoted_or_list.py
+++ /dev/null
@@ -1,24 +0,0 @@
-from pytest import raises
-
-from ..quoted_or_list import quoted_or_list
-
-
-def test_does_not_accept_an_empty_list():
-    with raises(StopIteration):
-        quoted_or_list([])
-
-
-def test_returns_single_quoted_item():
-    assert quoted_or_list(["A"]) == '"A"'
-
-
-def test_returns_two_item_list():
-    assert quoted_or_list(["A", "B"]) == '"A" or "B"'
-
-
-def test_returns_comma_separated_many_item_list():
-    assert quoted_or_list(["A", "B", "C"]) == '"A", "B" or "C"'
-
-
-def test_limits_to_five_items():
-    assert quoted_or_list(["A", "B", "C", "D", "E", "F"]) == '"A", "B", "C", "D" or "E"'
diff --git a/graphql/utils/tests/test_schema_printer.py b/graphql/utils/tests/test_schema_printer.py
deleted file mode 100644
index 0d61fac..0000000
--- a/graphql/utils/tests/test_schema_printer.py
+++ /dev/null
@@ -1,668 +0,0 @@
-from collections import OrderedDict
-
-from graphql.type import (
-    GraphQLBoolean,
-    GraphQLEnumType,
-    GraphQLInputObjectType,
-    GraphQLInt,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLScalarType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-)
-from graphql.type.definition import (
-    GraphQLArgument,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLInputObjectField,
-)
-from graphql.utils.schema_printer import print_introspection_schema, print_schema
-
-
-def print_for_test(schema):
-    return "\n" + print_schema(schema)
-
-
-def print_single_field_schema(field_config):
-    Root = GraphQLObjectType(name="Root", fields={"singleField": field_config})
-    return print_for_test(GraphQLSchema(Root))
-
-
-def test_prints_string_field():
-    output = print_single_field_schema(GraphQLField(GraphQLString))
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField: String
-}
-"""
-    )
-
-
-def test_prints_list_string_field():
-    output = print_single_field_schema(GraphQLField(GraphQLList(GraphQLString)))
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField: [String]
-}
-"""
-    )
-
-
-def test_prints_non_null_list_string_field():
-    output = print_single_field_schema(
-        GraphQLField(GraphQLNonNull(GraphQLList(GraphQLString)))
-    )
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField: [String]!
-}
-"""
-    )
-
-
-def test_prints_list_non_null_string_field():
-    output = print_single_field_schema(
-        GraphQLField((GraphQLList(GraphQLNonNull(GraphQLString))))
-    )
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField: [String!]
-}
-"""
-    )
-
-
-def test_prints_non_null_list_non_null_string_field():
-    output = print_single_field_schema(
-        GraphQLField(GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString))))
-    )
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField: [String!]!
-}
-"""
-    )
-
-
-def test_prints_object_field():
-    FooType = GraphQLObjectType(name="Foo", fields={"str": GraphQLField(GraphQLString)})
-
-    Root = GraphQLObjectType(name="Root", fields={"foo": GraphQLField(FooType)})
-
-    Schema = GraphQLSchema(Root)
-
-    output = print_for_test(Schema)
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Foo {
-  str: String
-}
-
-type Root {
-  foo: Foo
-}
-"""
-    )
-
-
-def test_prints_string_field_with_int_arg():
-    output = print_single_field_schema(
-        GraphQLField(type=GraphQLString, args={"argOne": GraphQLArgument(GraphQLInt)})
-    )
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField(argOne: Int): String
-}
-"""
-    )
-
-
-def test_prints_string_field_with_int_arg_with_default():
-    output = print_single_field_schema(
-        GraphQLField(
-            type=GraphQLString,
-            args={"argOne": GraphQLArgument(GraphQLInt, default_value=2)},
-        )
-    )
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField(argOne: Int = 2): String
-}
-"""
-    )
-
-
-def test_prints_string_field_with_non_null_int_arg():
-    output = print_single_field_schema(
-        GraphQLField(
-            type=GraphQLString,
-            args={"argOne": GraphQLArgument(GraphQLNonNull(GraphQLInt))},
-        )
-    )
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField(argOne: Int!): String
-}
-"""
-    )
-
-
-def test_prints_string_field_with_multiple_args():
-    output = print_single_field_schema(
-        GraphQLField(
-            type=GraphQLString,
-            args=OrderedDict(
-                [
-                    ("argOne", GraphQLArgument(GraphQLInt)),
-                    ("argTwo", GraphQLArgument(GraphQLString)),
-                ]
-            ),
-        )
-    )
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField(argOne: Int, argTwo: String): String
-}
-"""
-    )
-
-
-def test_prints_string_field_with_multiple_args_first_is_default():
-    output = print_single_field_schema(
-        GraphQLField(
-            type=GraphQLString,
-            args=OrderedDict(
-                [
-                    ("argOne", GraphQLArgument(GraphQLInt, default_value=1)),
-                    ("argTwo", GraphQLArgument(GraphQLString)),
-                    ("argThree", GraphQLArgument(GraphQLBoolean)),
-                ]
-            ),
-        )
-    )
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField(argOne: Int = 1, argTwo: String, argThree: Boolean): String
-}
-"""
-    )
-
-
-def test_prints_string_field_with_multiple_args_second_is_default():
-    output = print_single_field_schema(
-        GraphQLField(
-            type=GraphQLString,
-            args=OrderedDict(
-                [
-                    ("argOne", GraphQLArgument(GraphQLInt)),
-                    ("argTwo", GraphQLArgument(GraphQLString, default_value="foo")),
-                    ("argThree", GraphQLArgument(GraphQLBoolean)),
-                ]
-            ),
-        )
-    )
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField(argOne: Int, argTwo: String = "foo", argThree: Boolean): String
-}
-"""
-    )
-
-
-def test_prints_string_field_with_multiple_args_last_is_default():
-    output = print_single_field_schema(
-        GraphQLField(
-            type=GraphQLString,
-            args=OrderedDict(
-                [
-                    ("argOne", GraphQLArgument(GraphQLInt)),
-                    ("argTwo", GraphQLArgument(GraphQLString)),
-                    ("argThree", GraphQLArgument(GraphQLBoolean, default_value=False)),
-                ]
-            ),
-        )
-    )
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Root {
-  singleField(argOne: Int, argTwo: String, argThree: Boolean = false): String
-}
-"""
-    )
-
-
-def test_prints_interface():
-    FooType = GraphQLInterfaceType(
-        name="Foo",
-        resolve_type=lambda *_: None,
-        fields={"str": GraphQLField(GraphQLString)},
-    )
-
-    BarType = GraphQLObjectType(
-        name="Bar", fields={"str": GraphQLField(GraphQLString)}, interfaces=[FooType]
-    )
-
-    Root = GraphQLObjectType(name="Root", fields={"bar": GraphQLField(BarType)})
-
-    Schema = GraphQLSchema(Root, types=[BarType])
-    output = print_for_test(Schema)
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Bar implements Foo {
-  str: String
-}
-
-interface Foo {
-  str: String
-}
-
-type Root {
-  bar: Bar
-}
-"""
-    )
-
-
-def test_prints_multiple_interfaces():
-    FooType = GraphQLInterfaceType(
-        name="Foo",
-        resolve_type=lambda *_: None,
-        fields={"str": GraphQLField(GraphQLString)},
-    )
-    BaazType = GraphQLInterfaceType(
-        name="Baaz",
-        resolve_type=lambda *_: None,
-        fields={"int": GraphQLField(GraphQLInt)},
-    )
-
-    BarType = GraphQLObjectType(
-        name="Bar",
-        fields=OrderedDict(
-            [("str", GraphQLField(GraphQLString)), ("int", GraphQLField(GraphQLInt))]
-        ),
-        interfaces=[FooType, BaazType],
-    )
-
-    Root = GraphQLObjectType(name="Root", fields={"bar": GraphQLField(BarType)})
-
-    Schema = GraphQLSchema(Root, types=[BarType])
-    output = print_for_test(Schema)
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-interface Baaz {
-  int: Int
-}
-
-type Bar implements Foo, Baaz {
-  str: String
-  int: Int
-}
-
-interface Foo {
-  str: String
-}
-
-type Root {
-  bar: Bar
-}
-"""
-    )
-
-
-def test_prints_unions():
-    FooType = GraphQLObjectType(
-        name="Foo", fields={"bool": GraphQLField(GraphQLBoolean)}
-    )
-
-    BarType = GraphQLObjectType(name="Bar", fields={"str": GraphQLField(GraphQLString)})
-
-    SingleUnion = GraphQLUnionType(
-        name="SingleUnion", resolve_type=lambda *_: None, types=[FooType]
-    )
-
-    MultipleUnion = GraphQLUnionType(
-        name="MultipleUnion", resolve_type=lambda *_: None, types=[FooType, BarType]
-    )
-
-    Root = GraphQLObjectType(
-        name="Root",
-        fields=OrderedDict(
-            [
-                ("single", GraphQLField(SingleUnion)),
-                ("multiple", GraphQLField(MultipleUnion)),
-            ]
-        ),
-    )
-
-    Schema = GraphQLSchema(Root)
-    output = print_for_test(Schema)
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-type Bar {
-  str: String
-}
-
-type Foo {
-  bool: Boolean
-}
-
-union MultipleUnion = Foo | Bar
-
-type Root {
-  single: SingleUnion
-  multiple: MultipleUnion
-}
-
-union SingleUnion = Foo
-"""
-    )
-
-
-def test_prints_input_type():
-    InputType = GraphQLInputObjectType(
-        name="InputType", fields={"int": GraphQLInputObjectField(GraphQLInt)}
-    )
-
-    Root = GraphQLObjectType(
-        name="Root",
-        fields={
-            "str": GraphQLField(
-                GraphQLString, args={"argOne": GraphQLArgument(InputType)}
-            )
-        },
-    )
-
-    Schema = GraphQLSchema(Root)
-    output = print_for_test(Schema)
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-input InputType {
-  int: Int
-}
-
-type Root {
-  str(argOne: InputType): String
-}
-"""
-    )
-
-
-def test_prints_custom_scalar():
-    OddType = GraphQLScalarType(
-        name="Odd", serialize=lambda v: v if v % 2 == 1 else None
-    )
-
-    Root = GraphQLObjectType(name="Root", fields={"odd": GraphQLField(OddType)})
-
-    Schema = GraphQLSchema(Root)
-    output = print_for_test(Schema)
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-scalar Odd
-
-type Root {
-  odd: Odd
-}
-"""
-    )
-
-
-def test_print_enum():
-    RGBType = GraphQLEnumType(
-        name="RGB",
-        values=OrderedDict(
-            [
-                ("RED", GraphQLEnumValue(0)),
-                ("GREEN", GraphQLEnumValue(1)),
-                ("BLUE", GraphQLEnumValue(2)),
-            ]
-        ),
-    )
-
-    Root = GraphQLObjectType(name="Root", fields={"rgb": GraphQLField(RGBType)})
-
-    Schema = GraphQLSchema(Root)
-    output = print_for_test(Schema)
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-enum RGB {
-  RED
-  GREEN
-  BLUE
-}
-
-type Root {
-  rgb: RGB
-}
-"""
-    )
-
-
-def test_print_introspection_schema():
-    Root = GraphQLObjectType(
-        name="Root", fields={"onlyField": GraphQLField(GraphQLString)}
-    )
-
-    Schema = GraphQLSchema(Root)
-    output = "\n" + print_introspection_schema(Schema)
-
-    assert (
-        output
-        == """
-schema {
-  query: Root
-}
-
-directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
-
-directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
-
-directive @deprecated(reason: String = "No longer supported") on FIELD_DEFINITION | ENUM_VALUE
-
-type __Directive {
-  name: String!
-  description: String
-  locations: [__DirectiveLocation!]!
-  args: [__InputValue!]!
-  onOperation: Boolean! @deprecated(reason: "Use `locations`.")
-  onFragment: Boolean! @deprecated(reason: "Use `locations`.")
-  onField: Boolean! @deprecated(reason: "Use `locations`.")
-}
-
-enum __DirectiveLocation {
-  QUERY
-  MUTATION
-  SUBSCRIPTION
-  FIELD
-  FRAGMENT_DEFINITION
-  FRAGMENT_SPREAD
-  INLINE_FRAGMENT
-  SCHEMA
-  SCALAR
-  OBJECT
-  FIELD_DEFINITION
-  ARGUMENT_DEFINITION
-  INTERFACE
-  UNION
-  ENUM
-  ENUM_VALUE
-  INPUT_OBJECT
-  INPUT_FIELD_DEFINITION
-}
-
-type __EnumValue {
-  name: String!
-  description: String
-  isDeprecated: Boolean!
-  deprecationReason: String
-}
-
-type __Field {
-  name: String!
-  description: String
-  args: [__InputValue!]!
-  type: __Type!
-  isDeprecated: Boolean!
-  deprecationReason: String
-}
-
-type __InputValue {
-  name: String!
-  description: String
-  type: __Type!
-  defaultValue: String
-}
-
-type __Schema {
-  types: [__Type!]!
-  queryType: __Type!
-  mutationType: __Type
-  subscriptionType: __Type
-  directives: [__Directive!]!
-}
-
-type __Type {
-  kind: __TypeKind!
-  name: String
-  description: String
-  fields(includeDeprecated: Boolean = false): [__Field!]
-  interfaces: [__Type!]
-  possibleTypes: [__Type!]
-  enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
-  inputFields: [__InputValue!]
-  ofType: __Type
-}
-
-enum __TypeKind {
-  SCALAR
-  OBJECT
-  INTERFACE
-  UNION
-  ENUM
-  INPUT_OBJECT
-  LIST
-  NON_NULL
-}
-"""
-    )
diff --git a/graphql/utils/tests/test_suggestion_list.py b/graphql/utils/tests/test_suggestion_list.py
deleted file mode 100644
index 90043da..0000000
--- a/graphql/utils/tests/test_suggestion_list.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from graphql.utils.suggestion_list import suggestion_list
-
-
-def test_returns_results_when_input_is_empty():
-    assert suggestion_list("", ["a"]) == ["a"]
-
-
-def test_returns_empty_array_when_there_are_no_options():
-    assert suggestion_list("input", []) == []
-
-
-def test_returns_options_sorted_based_on_similarity():
-    assert suggestion_list("abc", ["a", "ab", "abc"]) == ["abc", "ab"]
-
-    assert suggestion_list("csutomer", ["customer", "stomer", "store"]) == [
-        "customer",
-        "stomer",
-        "store",
-    ]
diff --git a/graphql/utils/tests/test_type_comparators.py b/graphql/utils/tests/test_type_comparators.py
deleted file mode 100644
index 82174da..0000000
--- a/graphql/utils/tests/test_type_comparators.py
+++ /dev/null
@@ -1,107 +0,0 @@
-from collections import OrderedDict
-
-from graphql.type import (
-    GraphQLField,
-    GraphQLFloat,
-    GraphQLInt,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-)
-
-from ..type_comparators import is_equal_type, is_type_sub_type_of
-
-
-def _test_schema(field_type):
-    return GraphQLSchema(
-        query=GraphQLObjectType(
-            name="Query", fields=OrderedDict([("field", GraphQLField(field_type))])
-        )
-    )
-
-
-def test_is_equal_type_same_reference_are_equal():
-    assert is_equal_type(GraphQLString, GraphQLString)
-
-
-def test_is_equal_type_int_and_float_are_not_equal():
-    assert not is_equal_type(GraphQLInt, GraphQLFloat)
-
-
-def test_is_equal_type_lists_of_same_type_are_equal():
-    assert is_equal_type(GraphQLList(GraphQLInt), GraphQLList(GraphQLInt))
-
-
-def test_is_equal_type_lists_is_not_equal_to_item():
-    assert not is_equal_type(GraphQLList(GraphQLInt), GraphQLInt)
-
-
-def test_is_equal_type_nonnull_of_same_type_are_equal():
-    assert is_equal_type(GraphQLNonNull(GraphQLInt), GraphQLNonNull(GraphQLInt))
-
-
-def test_is_equal_type_nonnull_is_not_equal_to_nullable():
-    assert not is_equal_type(GraphQLNonNull(GraphQLInt), GraphQLInt)
-
-
-def test_is_equal_type_nonnull_is_not_equal_to_nullable():
-    assert not is_equal_type(GraphQLNonNull(GraphQLInt), GraphQLInt)
-
-
-def test_is_type_sub_type_of_same_reference_is_subtype():
-    schema = _test_schema(GraphQLString)
-    assert is_type_sub_type_of(schema, GraphQLString, GraphQLString)
-
-
-def test_is_type_sub_type_of_int_is_not_subtype_of_float():
-    schema = _test_schema(GraphQLString)
-    assert not is_type_sub_type_of(schema, GraphQLInt, GraphQLFloat)
-
-
-def test_is_type_sub_type_of_non_null_is_subtype_of_nullable():
-    schema = _test_schema(GraphQLString)
-    assert is_type_sub_type_of(schema, GraphQLNonNull(GraphQLInt), GraphQLInt)
-
-
-def test_is_type_sub_type_of_nullable_is_not_subtype_of_non_null():
-    schema = _test_schema(GraphQLString)
-    assert not is_type_sub_type_of(schema, GraphQLInt, GraphQLNonNull(GraphQLInt))
-
-
-def test_is_type_sub_type_of_item_is_not_subtype_of_list():
-    schema = _test_schema(GraphQLString)
-    assert not is_type_sub_type_of(schema, GraphQLInt, GraphQLList(GraphQLInt))
-
-
-def test_is_type_sub_type_of_list_is_not_subtype_of_item():
-    schema = _test_schema(GraphQLString)
-    assert not is_type_sub_type_of(schema, GraphQLList(GraphQLInt), GraphQLInt)
-
-
-def test_is_type_sub_type_of_member_is_subtype_of_union():
-    member = GraphQLObjectType(
-        name="Object",
-        is_type_of=lambda *_: True,
-        fields={"field": GraphQLField(GraphQLString)},
-    )
-    union = GraphQLUnionType(name="Union", types=[member])
-    schema = _test_schema(union)
-    assert is_type_sub_type_of(schema, member, union)
-
-
-def test_is_type_sub_type_of_implementation_is_subtype_of_interface():
-    iface = GraphQLInterfaceType(
-        name="Interface", fields={"field": GraphQLField(GraphQLString)}
-    )
-    impl = GraphQLObjectType(
-        name="Object",
-        is_type_of=lambda *_: True,
-        interfaces=[iface],
-        fields={"field": GraphQLField(GraphQLString)},
-    )
-    schema = _test_schema(impl)
-    assert is_type_sub_type_of(schema, impl, iface)
diff --git a/graphql/utils/type_comparators.py b/graphql/utils/type_comparators.py
deleted file mode 100644
index dcda87d..0000000
--- a/graphql/utils/type_comparators.py
+++ /dev/null
@@ -1,98 +0,0 @@
-from ..type.definition import (
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLUnionType,
-    is_abstract_type,
-)
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..type.definition import GraphQLScalarType
-    from ..type.typemap import GraphQLTypeMap
-    from ..type.schema import GraphQLSchema
-    from typing import Union
-
-
-def is_equal_type(type_a, type_b):
-    if type_a is type_b:
-        return True
-
-    if isinstance(type_a, GraphQLNonNull) and isinstance(type_b, GraphQLNonNull):
-        return is_equal_type(type_a.of_type, type_b.of_type)
-
-    if isinstance(type_a, GraphQLList) and isinstance(type_b, GraphQLList):
-        return is_equal_type(type_a.of_type, type_b.of_type)
-
-    return False
-
-
-def is_type_sub_type_of(schema, maybe_subtype, super_type):
-    # type: (Union[GraphQLSchema, GraphQLTypeMap], GraphQLScalarType, GraphQLScalarType) -> bool
-    if maybe_subtype is super_type:
-        return True
-
-    if isinstance(super_type, GraphQLNonNull):
-        if isinstance(maybe_subtype, GraphQLNonNull):
-            return is_type_sub_type_of(
-                schema, maybe_subtype.of_type, super_type.of_type
-            )
-        return False
-    elif isinstance(maybe_subtype, GraphQLNonNull):
-        return is_type_sub_type_of(schema, maybe_subtype.of_type, super_type)
-
-    if isinstance(super_type, GraphQLList):
-        if isinstance(maybe_subtype, GraphQLList):
-            return is_type_sub_type_of(
-                schema, maybe_subtype.of_type, super_type.of_type
-            )
-        return False
-    elif isinstance(maybe_subtype, GraphQLList):
-        return False
-
-    if (
-        is_abstract_type(super_type)
-        and isinstance(maybe_subtype, GraphQLObjectType)
-        and schema.is_possible_type(super_type, maybe_subtype)
-    ):
-        return True
-
-    return False
-
-
-def do_types_overlap(
-    schema,  # type: GraphQLSchema
-    t1,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-    t2,  # type: Union[GraphQLInterfaceType, GraphQLUnionType]
-):
-    # type: (...) -> bool
-    # print 'do_types_overlap', t1, t2
-    if t1 == t2:
-        # print '1'
-        return True
-
-    if isinstance(t1, (GraphQLInterfaceType, GraphQLUnionType)):
-        if isinstance(t2, (GraphQLInterfaceType, GraphQLUnionType)):
-            # If both types are abstract, then determine if there is any intersection
-            # between possible concrete types of each.
-            s = any(
-                [
-                    schema.is_possible_type(t2, type)
-                    for type in schema.get_possible_types(t1)
-                ]
-            )
-            # print '2',s
-            return s
-        # Determine if the latter type is a possible concrete type of the former.
-        r = schema.is_possible_type(t1, t2)
-        # print '3', r
-        return r
-
-    if isinstance(t2, (GraphQLInterfaceType, GraphQLUnionType)):
-        t = schema.is_possible_type(t2, t1)
-        # print '4', t
-        return t
-
-    # print '5'
-    return False
diff --git a/graphql/utils/type_from_ast.py b/graphql/utils/type_from_ast.py
deleted file mode 100644
index 6868c13..0000000
--- a/graphql/utils/type_from_ast.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from ..language import ast
-from ..type.definition import GraphQLList, GraphQLNonNull
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.ast import ListType, NamedType, NonNullType
-    from ..type.definition import GraphQLNamedType
-    from ..type.schema import GraphQLSchema
-    from typing import Union
-
-
-def type_from_ast(schema, type_node):
-    # type: (GraphQLSchema, Union[ListType, NamedType, NonNullType]) -> Union[GraphQLList, GraphQLNonNull, GraphQLNamedType]
-    if isinstance(type_node, ast.ListType):
-        inner_type = type_from_ast(schema, type_node.type)
-        return inner_type and GraphQLList(inner_type)
-
-    elif isinstance(type_node, ast.NonNullType):
-        inner_type = type_from_ast(schema, type_node.type)
-        return inner_type and GraphQLNonNull(inner_type)  # type: ignore
-
-    elif isinstance(type_node, ast.NamedType):
-        schema_type = schema.get_type(type_node.name.value)
-        return schema_type  # type: ignore
-
-    raise Exception("Unexpected type kind: {type_kind}".format(type_kind=type_node))
diff --git a/graphql/utils/type_info.py b/graphql/utils/type_info.py
deleted file mode 100644
index e224ff3..0000000
--- a/graphql/utils/type_info.py
+++ /dev/null
@@ -1,224 +0,0 @@
-import six
-
-from ..language import visitor_meta
-from ..type.definition import (
-    GraphQLInputObjectType,
-    GraphQLList,
-    get_named_type,
-    get_nullable_type,
-    is_composite_type,
-)
-from .get_field_def import get_field_def
-from .type_from_ast import type_from_ast
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..type.schema import GraphQLSchema
-    from ..type.definition import (
-        GraphQLType,
-        GraphQLInterfaceType,
-        GraphQLObjectType,
-        GraphQLField,
-        GraphQLArgument,
-        GraphQLNonNull,
-        GraphQLScalarType,
-    )
-    from ..type.directives import GraphQLDirective
-    from ..language.ast import (
-        SelectionSet,
-        Field,
-        OperationDefinition,
-        InlineFragment,
-        Argument,
-        ObjectField,
-    )
-    from typing import Callable, Optional, Any, List, Union
-
-
-def pop(lst):
-    # type: (Any) -> None
-    if lst:
-        lst.pop()
-
-
-# noinspection PyPep8Naming
-@six.add_metaclass(visitor_meta.VisitorMeta)
-class TypeInfo(object):
-    __slots__ = (
-        "_schema",
-        "_type_stack",
-        "_parent_type_stack",
-        "_input_type_stack",
-        "_field_def_stack",
-        "_directive",
-        "_argument",
-        "_get_field_def_fn",
-    )
-
-    def __init__(self, schema, get_field_def_fn=get_field_def):
-        # type: (GraphQLSchema, Callable) -> None
-        self._schema = schema
-        self._type_stack = []  # type: List[Optional[GraphQLType]]
-        self._parent_type_stack = (
-            []
-        )  # type: List[Union[GraphQLInterfaceType, GraphQLObjectType, None]]
-        self._input_type_stack = (
-            []
-        )  # type: List[Optional[Union[GraphQLInputObjectType, GraphQLNonNull, GraphQLList, GraphQLScalarType, None]]]
-        self._field_def_stack = []  # type: List[Optional[GraphQLField]]
-        self._directive = None  # type: Optional[GraphQLDirective]
-        self._argument = None  # type: Optional[GraphQLArgument]
-        self._get_field_def_fn = get_field_def_fn
-
-    def get_type(self):
-        # type: () -> Optional[GraphQLType]
-        if self._type_stack:
-            return self._type_stack[-1]
-        return None
-
-    def get_parent_type(self):
-        # type: () -> Union[GraphQLInterfaceType, GraphQLObjectType, None]
-        if self._parent_type_stack:
-            return self._parent_type_stack[-1]
-        return None
-
-    def get_input_type(self):
-        # type: () -> Union[GraphQLInputObjectType, GraphQLNonNull, GraphQLList, GraphQLScalarType, None]
-        if self._input_type_stack:
-            return self._input_type_stack[-1]
-        return None
-
-    def get_field_def(self):
-        # type: () -> Optional[GraphQLField]
-        if self._field_def_stack:
-            return self._field_def_stack[-1]
-        return None
-
-    def get_directive(self):
-        # type: () -> Optional[Any]
-        return self._directive
-
-    def get_argument(self):
-        # type: () -> Optional[GraphQLArgument]
-        return self._argument
-
-    def leave(self, node):
-        # type: (Any) -> Optional[Any]
-        method = self._get_leave_handler(type(node))  # type: ignore
-        if method:
-            return method(self)
-        return None
-
-    def enter(self, node):
-        # type: (Any) -> Optional[Any]
-        method = self._get_enter_handler(type(node))  # type: ignore
-        if method:
-            return method(self, node)
-        return None
-
-    def enter_SelectionSet(self, node):
-        # type: (SelectionSet) -> None
-        named_type = get_named_type(self.get_type())
-        composite_type = None
-        if is_composite_type(named_type):
-            composite_type = named_type
-        self._parent_type_stack.append(composite_type)  # type: ignore
-
-    def enter_Field(self, node):
-        # type: (Field) -> None
-        parent_type = self.get_parent_type()
-        field_def = None
-        if parent_type:
-            field_def = self._get_field_def_fn(self._schema, parent_type, node)
-        self._field_def_stack.append(field_def)
-        self._type_stack.append(field_def.type if field_def else None)
-
-    def enter_Directive(self, node):
-        self._directive = self._schema.get_directive(node.name.value)
-
-    def enter_OperationDefinition(self, node):
-        # type: (OperationDefinition) -> None
-        definition_type = None
-        if node.operation == "query":
-            definition_type = self._schema.get_query_type()
-        elif node.operation == "mutation":
-            definition_type = self._schema.get_mutation_type()
-        elif node.operation == "subscription":
-            definition_type = self._schema.get_subscription_type()
-
-        self._type_stack.append(definition_type)
-
-    def enter_InlineFragment(self, node):
-        # type: (InlineFragment) -> None
-        type_condition_ast = node.type_condition
-        type = (
-            type_from_ast(self._schema, type_condition_ast)
-            if type_condition_ast
-            else self.get_type()
-        )
-        self._type_stack.append(type)
-
-    enter_FragmentDefinition = enter_InlineFragment
-
-    def enter_VariableDefinition(self, node):
-        self._input_type_stack.append(type_from_ast(self._schema, node.type))
-
-    def enter_Argument(self, node):
-        # type: (Argument) -> None
-        arg_def = None
-        arg_type = None
-        field_or_directive = self.get_directive() or self.get_field_def()
-        if field_or_directive:
-            arg_def = field_or_directive.args.get(node.name.value)
-            if arg_def:
-                arg_type = arg_def.type
-        self._argument = arg_def
-        self._input_type_stack.append(arg_type)
-
-    def enter_ListValue(self, node):
-        list_type = get_nullable_type(self.get_input_type())
-        self._input_type_stack.append(
-            list_type.of_type if isinstance(list_type, GraphQLList) else None
-        )
-
-    def enter_ObjectField(self, node):
-        # type: (ObjectField) -> None
-        object_type = get_named_type(self.get_input_type())
-        field_type = None
-        if isinstance(object_type, GraphQLInputObjectType):
-            input_field = object_type.fields.get(node.name.value)
-            field_type = input_field.type if input_field else None
-        self._input_type_stack.append(field_type)
-
-    def leave_SelectionSet(self):
-        # type: () -> None
-        pop(self._parent_type_stack)
-
-    def leave_Field(self):
-        # type: () -> None
-        pop(self._field_def_stack)
-        pop(self._type_stack)
-
-    def leave_Directive(self):
-        self._directive = None
-
-    def leave_OperationDefinition(self):
-        # type: () -> None
-        pop(self._type_stack)
-
-    leave_InlineFragment = leave_OperationDefinition
-    leave_FragmentDefinition = leave_OperationDefinition
-
-    def leave_VariableDefinition(self):
-        pop(self._input_type_stack)
-
-    def leave_Argument(self):
-        # type: () -> None
-        self._argument = None
-        pop(self._input_type_stack)
-
-    def leave_ListValue(self):
-        # type: () -> None
-        pop(self._input_type_stack)
-
-    leave_ObjectField = leave_ListValue
diff --git a/graphql/utils/undefined.py b/graphql/utils/undefined.py
deleted file mode 100644
index db8f80a..0000000
--- a/graphql/utils/undefined.py
+++ /dev/null
@@ -1,15 +0,0 @@
-class _Undefined(object):
-    """A representation of an Undefined value distinct from a None value"""
-
-    def __bool__(self):
-        # type: () -> bool
-        return False
-
-    __nonzero__ = __bool__
-
-    def __repr__(self):
-        # type: () -> str
-        return "Undefined"
-
-
-Undefined = _Undefined()
diff --git a/graphql/utils/value_from_ast.py b/graphql/utils/value_from_ast.py
deleted file mode 100644
index 7ad52bc..0000000
--- a/graphql/utils/value_from_ast.py
+++ /dev/null
@@ -1,81 +0,0 @@
-from ..language import ast
-from ..type import (
-    GraphQLEnumType,
-    GraphQLInputObjectType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLScalarType,
-)
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..language.ast import Node
-    from ..type.definition import GraphQLType
-    from typing import Dict, Union, Optional, List
-
-
-def value_from_ast(value_ast, type, variables=None):
-    # type: (Optional[Node], GraphQLType, Optional[Dict[str, Union[List, Dict, int, float, bool, str, None]]]) -> Union[List, Dict, int, float, bool, str, None]
-    """Given a type and a value AST node known to match this type, build a
-    runtime value."""
-    if isinstance(type, GraphQLNonNull):
-        # Note: we're not checking that the result of coerceValueAST is non-null.
-        # We're assuming that this query has been validated and the value used here is of the correct type.
-        return value_from_ast(value_ast, type.of_type, variables)
-
-    if value_ast is None:
-        return None
-
-    if isinstance(value_ast, ast.Variable):
-        variable_name = value_ast.name.value
-        if not variables or variable_name not in variables:
-            return None
-
-        # Note: we're not doing any checking that this variable is correct. We're assuming that this query
-        # has been validated and the variable usage here is of the correct type.
-        return variables.get(variable_name)
-
-    if isinstance(type, GraphQLList):
-        item_type = type.of_type
-        if isinstance(value_ast, ast.ListValue):
-            return [
-                value_from_ast(item_ast, item_type, variables)
-                for item_ast in value_ast.values
-            ]
-
-        else:
-            return [value_from_ast(value_ast, item_type, variables)]
-
-    if isinstance(type, GraphQLInputObjectType):
-        fields = type.fields
-        if not isinstance(value_ast, ast.ObjectValue):
-            return None
-
-        field_asts = {}
-
-        for field_ast in value_ast.fields:
-            field_asts[field_ast.name.value] = field_ast
-
-        obj = {}
-        for field_name, field in fields.items():
-            if field_name not in field_asts:
-                if field.default_value is not None:
-                    # We use out_name as the output name for the
-                    # dict if exists
-                    obj[field.out_name or field_name] = field.default_value
-
-                continue
-
-            field_ast = field_asts[field_name]
-            field_value_ast = field_ast.value
-            field_value = value_from_ast(field_value_ast, field.type, variables)
-
-            # We use out_name as the output name for the
-            # dict if exists
-            obj[field.out_name or field_name] = field_value
-
-        return type.create_container(obj)
-
-    assert isinstance(type, (GraphQLScalarType, GraphQLEnumType)), "Must be input type"
-
-    return type.parse_literal(value_ast)
diff --git a/graphql/validation/__init__.py b/graphql/validation/__init__.py
deleted file mode 100644
index ce474e1..0000000
--- a/graphql/validation/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-from .validation import validate
-from .rules import specified_rules
-
-__all__ = ["validate", "specified_rules"]
diff --git a/graphql/validation/rules/__init__.py b/graphql/validation/rules/__init__.py
deleted file mode 100644
index 056a530..0000000
--- a/graphql/validation/rules/__init__.py
+++ /dev/null
@@ -1,85 +0,0 @@
-from .arguments_of_correct_type import ArgumentsOfCorrectType
-from .default_values_of_correct_type import DefaultValuesOfCorrectType
-from .fields_on_correct_type import FieldsOnCorrectType
-from .fragments_on_composite_types import FragmentsOnCompositeTypes
-from .known_argument_names import KnownArgumentNames
-from .known_directives import KnownDirectives
-from .known_fragment_names import KnownFragmentNames
-from .known_type_names import KnownTypeNames
-from .lone_anonymous_operation import LoneAnonymousOperation
-from .no_fragment_cycles import NoFragmentCycles
-from .no_undefined_variables import NoUndefinedVariables
-from .no_unused_fragments import NoUnusedFragments
-from .no_unused_variables import NoUnusedVariables
-from .overlapping_fields_can_be_merged import OverlappingFieldsCanBeMerged
-from .possible_fragment_spreads import PossibleFragmentSpreads
-from .provided_non_null_arguments import ProvidedNonNullArguments
-from .scalar_leafs import ScalarLeafs
-from .unique_argument_names import UniqueArgumentNames
-from .unique_fragment_names import UniqueFragmentNames
-from .unique_input_field_names import UniqueInputFieldNames
-from .unique_operation_names import UniqueOperationNames
-from .unique_variable_names import UniqueVariableNames
-from .variables_are_input_types import VariablesAreInputTypes
-from .variables_in_allowed_position import VariablesInAllowedPosition
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import List, Type
-    from .base import ValidationRule
-
-
-specified_rules = [
-    UniqueOperationNames,
-    LoneAnonymousOperation,
-    KnownTypeNames,
-    FragmentsOnCompositeTypes,
-    VariablesAreInputTypes,
-    ScalarLeafs,
-    FieldsOnCorrectType,
-    UniqueFragmentNames,
-    KnownFragmentNames,
-    NoUnusedFragments,
-    PossibleFragmentSpreads,
-    NoFragmentCycles,
-    NoUndefinedVariables,
-    NoUnusedVariables,
-    KnownDirectives,
-    KnownArgumentNames,
-    UniqueArgumentNames,
-    ArgumentsOfCorrectType,
-    ProvidedNonNullArguments,
-    DefaultValuesOfCorrectType,
-    VariablesInAllowedPosition,
-    OverlappingFieldsCanBeMerged,
-    UniqueInputFieldNames,
-    UniqueVariableNames,
-]  # type: List[Type[ValidationRule]]
-
-__all__ = [
-    "ArgumentsOfCorrectType",
-    "DefaultValuesOfCorrectType",
-    "FieldsOnCorrectType",
-    "FragmentsOnCompositeTypes",
-    "KnownArgumentNames",
-    "KnownDirectives",
-    "KnownFragmentNames",
-    "KnownTypeNames",
-    "LoneAnonymousOperation",
-    "NoFragmentCycles",
-    "UniqueVariableNames",
-    "NoUndefinedVariables",
-    "NoUnusedFragments",
-    "NoUnusedVariables",
-    "OverlappingFieldsCanBeMerged",
-    "PossibleFragmentSpreads",
-    "ProvidedNonNullArguments",
-    "ScalarLeafs",
-    "UniqueArgumentNames",
-    "UniqueFragmentNames",
-    "UniqueInputFieldNames",
-    "UniqueOperationNames",
-    "VariablesAreInputTypes",
-    "VariablesInAllowedPosition",
-    "specified_rules",
-]
diff --git a/graphql/validation/rules/arguments_of_correct_type.py b/graphql/validation/rules/arguments_of_correct_type.py
deleted file mode 100644
index 7b9f87d..0000000
--- a/graphql/validation/rules/arguments_of_correct_type.py
+++ /dev/null
@@ -1,39 +0,0 @@
-from ...error import GraphQLError
-from ...language.printer import print_ast
-from ...utils.is_valid_literal_value import is_valid_literal_value
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import Argument
-    from typing import Any, List, Union
-
-
-class ArgumentsOfCorrectType(ValidationRule):
-    def enter_Argument(
-        self,
-        node,  # type: Argument
-        key,  # type: int
-        parent,  # type: List[Argument]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> bool
-        arg_def = self.context.get_argument()
-        if arg_def:
-            errors = is_valid_literal_value(arg_def.type, node.value)
-            if errors:
-                self.context.report_error(
-                    GraphQLError(
-                        self.bad_value_message(
-                            node.name.value, arg_def.type, print_ast(node.value), errors
-                        ),
-                        [node.value],
-                    )
-                )
-        return False
-
-    @staticmethod
-    def bad_value_message(arg_name, type, value, verbose_errors):
-        message = (u"\n" + u"\n".join(verbose_errors)) if verbose_errors else ""
-        return 'Argument "{}" has invalid value {}.{}'.format(arg_name, value, message)
diff --git a/graphql/validation/rules/base.py b/graphql/validation/rules/base.py
deleted file mode 100644
index 53541c9..0000000
--- a/graphql/validation/rules/base.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from ...language.visitor import Visitor
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-
-
-class ValidationRule(Visitor):
-    __slots__ = ("context",)
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        self.context = context
diff --git a/graphql/validation/rules/default_values_of_correct_type.py b/graphql/validation/rules/default_values_of_correct_type.py
deleted file mode 100644
index d35c087..0000000
--- a/graphql/validation/rules/default_values_of_correct_type.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from ...error import GraphQLError
-from ...language.printer import print_ast
-from ...type.definition import GraphQLNonNull
-from ...utils.is_valid_literal_value import is_valid_literal_value
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import Document, OperationDefinition, SelectionSet
-    from typing import List, Union
-
-
-class DefaultValuesOfCorrectType(ValidationRule):
-    def enter_VariableDefinition(self, node, key, parent, path, ancestors):
-        name = node.variable.name.value
-        default_value = node.default_value
-        type = self.context.get_input_type()
-
-        if isinstance(type, GraphQLNonNull) and default_value:
-            self.context.report_error(
-                GraphQLError(
-                    self.default_for_non_null_arg_message(name, type, type.of_type),
-                    [default_value],
-                )
-            )
-
-        if type and default_value:
-            errors = is_valid_literal_value(type, default_value)
-            if errors:
-                self.context.report_error(
-                    GraphQLError(
-                        self.bad_value_for_default_arg_message(
-                            name, type, print_ast(default_value), errors
-                        ),
-                        [default_value],
-                    )
-                )
-        return False
-
-    def enter_SelectionSet(
-        self,
-        node,  # type: SelectionSet
-        key,  # type: str
-        parent,  # type: OperationDefinition
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Union[List[OperationDefinition], Document]]
-    ):
-        # type: (...) -> bool
-        return False
-
-    def enter_FragmentDefinition(self, node, key, parent, path, ancestors):
-        return False
-
-    @staticmethod
-    def default_for_non_null_arg_message(var_name, type, guess_type):
-        return (
-            u'Variable "${}" of type "{}" is required and will not use the default value. '
-            u'Perhaps you meant to use type "{}".'.format(var_name, type, guess_type)
-        )
-
-    @staticmethod
-    def bad_value_for_default_arg_message(var_name, type, value, verbose_errors):
-        message = (u"\n" + u"\n".join(verbose_errors)) if verbose_errors else u""
-        return u'Variable "${}" of type "{}" has invalid default value: {}.{}'.format(
-            var_name, type, value, message
-        )
diff --git a/graphql/validation/rules/fields_on_correct_type.py b/graphql/validation/rules/fields_on_correct_type.py
deleted file mode 100644
index fdcec44..0000000
--- a/graphql/validation/rules/fields_on_correct_type.py
+++ /dev/null
@@ -1,141 +0,0 @@
-from collections import Counter
-
-from ...error import GraphQLError
-from ...pyutils.ordereddict import OrderedDict
-from ...type.definition import GraphQLInterfaceType, GraphQLObjectType, GraphQLUnionType
-from ...utils.quoted_or_list import quoted_or_list
-from ...utils.suggestion_list import suggestion_list
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import Field, InlineFragment
-    from typing import Any, List, Union
-
-try:
-    # Python 2
-    from itertools import izip  # type: ignore
-except ImportError:
-    # Python 3
-    izip = zip
-
-
-def _undefined_field_message(field_name, type, suggested_types, suggested_fields):
-    message = 'Cannot query field "{}" on type "{}".'.format(field_name, type)
-
-    if suggested_types:
-        suggestions = quoted_or_list(suggested_types)
-        message += " Did you mean to use an inline fragment on {}?".format(suggestions)
-    elif suggested_fields:
-        suggestions = quoted_or_list(suggested_fields)
-        message += " Did you mean {}?".format(suggestions)
-
-    return message
-
-
-class OrderedCounter(Counter, OrderedDict):
-    pass
-
-
-class FieldsOnCorrectType(ValidationRule):
-    """Fields on correct type
-
-      A GraphQL document is only valid if all fields selected are defined by the
-      parent type, or are an allowed meta field such as __typenamme
-    """
-
-    def enter_Field(
-        self,
-        node,  # type: Field
-        key,  # type: int
-        parent,  # type: Union[List[Union[Field, InlineFragment]], List[Field]]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        parent_type = self.context.get_parent_type()
-        if not parent_type:
-            return
-
-        field_def = self.context.get_field_def()
-        if not field_def:
-            #  This field doesn't exist, lets look for suggestions.
-            schema = self.context.get_schema()
-            field_name = node.name.value
-
-            # First determine if there are any suggested types to condition on.
-            suggested_type_names = get_suggested_type_names(
-                schema, parent_type, field_name
-            )
-            # if there are no suggested types perhaps it was a typo?
-            suggested_field_names = (
-                []
-                if suggested_type_names
-                else get_suggested_field_names(schema, parent_type, field_name)
-            )
-
-            # report an error including helpful suggestions.
-            self.context.report_error(
-                GraphQLError(
-                    _undefined_field_message(
-                        field_name,
-                        parent_type.name,
-                        suggested_type_names,
-                        suggested_field_names,
-                    ),
-                    [node],
-                )
-            )
-
-
-def get_suggested_type_names(schema, output_type, field_name):
-    """Go through all of the implementations of type, as well as the interfaces
-      that they implement. If any of those types include the provided field,
-      suggest them, sorted by how often the type is referenced,  starting
-      with Interfaces."""
-
-    if isinstance(output_type, (GraphQLInterfaceType, GraphQLUnionType)):
-        suggested_object_types = []
-        interface_usage_count = OrderedDict()
-        for possible_type in schema.get_possible_types(output_type):
-            if not possible_type.fields.get(field_name):
-                return
-
-            # This object type defines this field.
-            suggested_object_types.append(possible_type.name)
-
-            for possible_interface in possible_type.interfaces:
-                if not possible_interface.fields.get(field_name):
-                    continue
-
-                # This interface type defines this field.
-                interface_usage_count[possible_interface.name] = (
-                    interface_usage_count.get(possible_interface.name, 0) + 1
-                )
-
-        # Suggest interface types based on how common they are.
-        suggested_interface_types = sorted(
-            list(interface_usage_count.keys()),
-            key=lambda k: interface_usage_count[k],
-            reverse=True,
-        )
-
-        # Suggest both interface and object types.
-        suggested_interface_types.extend(suggested_object_types)
-        return suggested_interface_types
-
-    # Otherwise, must be an Object type, which does not have possible fields.
-    return []
-
-
-def get_suggested_field_names(schema, graphql_type, field_name):
-    """For the field name provided, determine if there are any similar field names
-    that may be the result of a typo."""
-
-    if isinstance(graphql_type, (GraphQLInterfaceType, GraphQLObjectType)):
-        possible_field_names = list(graphql_type.fields.keys())
-
-        return suggestion_list(field_name, possible_field_names)
-
-    # Otherwise, must be a Union type, which does not define fields.
-    return []
diff --git a/graphql/validation/rules/fragments_on_composite_types.py b/graphql/validation/rules/fragments_on_composite_types.py
deleted file mode 100644
index 33324b4..0000000
--- a/graphql/validation/rules/fragments_on_composite_types.py
+++ /dev/null
@@ -1,55 +0,0 @@
-from ...error import GraphQLError
-from ...language.printer import print_ast
-from ...type.definition import is_composite_type
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import Field, InlineFragment
-    from typing import Any, List, Union
-
-
-class FragmentsOnCompositeTypes(ValidationRule):
-    def enter_InlineFragment(
-        self,
-        node,  # type: InlineFragment
-        key,  # type: int
-        parent,  # type: Union[List[Union[Field, InlineFragment]], List[InlineFragment]]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        type = self.context.get_type()
-
-        if node.type_condition and type and not is_composite_type(type):
-            self.context.report_error(
-                GraphQLError(
-                    self.inline_fragment_on_non_composite_error_message(
-                        print_ast(node.type_condition)
-                    ),
-                    [node.type_condition],
-                )
-            )
-
-    def enter_FragmentDefinition(self, node, key, parent, path, ancestors):
-        type = self.context.get_type()
-
-        if type and not is_composite_type(type):
-            self.context.report_error(
-                GraphQLError(
-                    self.fragment_on_non_composite_error_message(
-                        node.name.value, print_ast(node.type_condition)
-                    ),
-                    [node.type_condition],
-                )
-            )
-
-    @staticmethod
-    def inline_fragment_on_non_composite_error_message(type):
-        return 'Fragment cannot condition on non composite type "{}".'.format(type)
-
-    @staticmethod
-    def fragment_on_non_composite_error_message(frag_name, type):
-        return 'Fragment "{}" cannot condition on non composite type "{}".'.format(
-            frag_name, type
-        )
diff --git a/graphql/validation/rules/known_argument_names.py b/graphql/validation/rules/known_argument_names.py
deleted file mode 100644
index 96966a8..0000000
--- a/graphql/validation/rules/known_argument_names.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from ...error import GraphQLError
-from ...language import ast
-from ...utils.quoted_or_list import quoted_or_list
-from ...utils.suggestion_list import suggestion_list
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import Argument
-    from typing import Any, List, Union
-
-
-def _unknown_arg_message(arg_name, field_name, type, suggested_args):
-    message = 'Unknown argument "{}" on field "{}" of type "{}".'.format(
-        arg_name, field_name, type
-    )
-    if suggested_args:
-        message += " Did you mean {}?".format(quoted_or_list(suggested_args))
-
-    return message
-
-
-def _unknown_directive_arg_message(arg_name, directive_name, suggested_args):
-    message = 'Unknown argument "{}" on directive "@{}".'.format(
-        arg_name, directive_name
-    )
-    if suggested_args:
-        message += " Did you mean {}?".format(quoted_or_list(suggested_args))
-
-    return message
-
-
-class KnownArgumentNames(ValidationRule):
-    def enter_Argument(
-        self,
-        node,  # type: Argument
-        key,  # type: int
-        parent,  # type: List[Argument]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        argument_of = ancestors[-1]
-
-        if isinstance(argument_of, ast.Field):
-            field_def = self.context.get_field_def()
-            if not field_def:
-                return
-
-            field_arg_def = field_def.args.get(node.name.value)
-
-            if not field_arg_def:
-                parent_type = self.context.get_parent_type()
-                assert parent_type
-                self.context.report_error(
-                    GraphQLError(
-                        _unknown_arg_message(
-                            node.name.value,
-                            argument_of.name.value,
-                            parent_type.name,
-                            suggestion_list(
-                                node.name.value,
-                                (arg_name for arg_name in field_def.args.keys()),
-                            ),
-                        ),
-                        [node],
-                    )
-                )
-
-        elif isinstance(argument_of, ast.Directive):
-            directive = self.context.get_directive()
-            if not directive:
-                return
-
-            directive_arg_def = directive.args.get(node.name.value)
-
-            if not directive_arg_def:
-                self.context.report_error(
-                    GraphQLError(
-                        _unknown_directive_arg_message(
-                            node.name.value,
-                            directive.name,
-                            suggestion_list(
-                                node.name.value,
-                                (arg_name for arg_name in directive.args.keys()),
-                            ),
-                        ),
-                        [node],
-                    )
-                )
diff --git a/graphql/validation/rules/known_directives.py b/graphql/validation/rules/known_directives.py
deleted file mode 100644
index 8672835..0000000
--- a/graphql/validation/rules/known_directives.py
+++ /dev/null
@@ -1,108 +0,0 @@
-from ...error import GraphQLError
-from ...language import ast
-from ...type.directives import DirectiveLocation
-from .base import ValidationRule
-
-
-class KnownDirectives(ValidationRule):
-    def enter_Directive(self, node, key, parent, path, ancestors):
-        directive_def = next(
-            (
-                definition
-                for definition in self.context.get_schema().get_directives()
-                if definition.name == node.name.value
-            ),
-            None,
-        )
-
-        if not directive_def:
-            return self.context.report_error(
-                GraphQLError(self.unknown_directive_message(node.name.value), [node])
-            )
-
-        candidate_location = get_directive_location_for_ast_path(ancestors)
-        if not candidate_location:
-            self.context.report_error(
-                GraphQLError(
-                    self.misplaced_directive_message(node.name.value, node.type), [node]
-                )
-            )
-        elif candidate_location not in directive_def.locations:
-            self.context.report_error(
-                GraphQLError(
-                    self.misplaced_directive_message(
-                        node.name.value, candidate_location
-                    ),
-                    [node],
-                )
-            )
-
-    @staticmethod
-    def unknown_directive_message(directive_name):
-        return 'Unknown directive "{}".'.format(directive_name)
-
-    @staticmethod
-    def misplaced_directive_message(directive_name, location):
-        return 'Directive "{}" may not be used on "{}".'.format(
-            directive_name, location
-        )
-
-
-_operation_definition_map = {
-    "query": DirectiveLocation.QUERY,
-    "mutation": DirectiveLocation.MUTATION,
-    "subscription": DirectiveLocation.SUBSCRIPTION,
-}
-
-
-def get_directive_location_for_ast_path(ancestors):
-    applied_to = ancestors[-1]
-    if isinstance(applied_to, ast.OperationDefinition):
-        return _operation_definition_map.get(applied_to.operation)
-
-    elif isinstance(applied_to, ast.Field):
-        return DirectiveLocation.FIELD
-
-    elif isinstance(applied_to, ast.FragmentSpread):
-        return DirectiveLocation.FRAGMENT_SPREAD
-
-    elif isinstance(applied_to, ast.InlineFragment):
-        return DirectiveLocation.INLINE_FRAGMENT
-
-    elif isinstance(applied_to, ast.FragmentDefinition):
-        return DirectiveLocation.FRAGMENT_DEFINITION
-
-    elif isinstance(applied_to, ast.SchemaDefinition):
-        return DirectiveLocation.SCHEMA
-
-    elif isinstance(applied_to, ast.ScalarTypeDefinition):
-        return DirectiveLocation.SCALAR
-
-    elif isinstance(applied_to, ast.ObjectTypeDefinition):
-        return DirectiveLocation.OBJECT
-
-    elif isinstance(applied_to, ast.FieldDefinition):
-        return DirectiveLocation.FIELD_DEFINITION
-
-    elif isinstance(applied_to, ast.InterfaceTypeDefinition):
-        return DirectiveLocation.INTERFACE
-
-    elif isinstance(applied_to, ast.UnionTypeDefinition):
-        return DirectiveLocation.UNION
-
-    elif isinstance(applied_to, ast.EnumTypeDefinition):
-        return DirectiveLocation.ENUM
-
-    elif isinstance(applied_to, ast.EnumValueDefinition):
-        return DirectiveLocation.ENUM_VALUE
-
-    elif isinstance(applied_to, ast.InputObjectTypeDefinition):
-        return DirectiveLocation.INPUT_OBJECT
-
-    elif isinstance(applied_to, ast.InputValueDefinition):
-        parent_node = ancestors[-3]
-        return (
-            DirectiveLocation.INPUT_FIELD_DEFINITION
-            if isinstance(parent_node, ast.InputObjectTypeDefinition)
-            else DirectiveLocation.ARGUMENT_DEFINITION
-        )
diff --git a/graphql/validation/rules/known_fragment_names.py b/graphql/validation/rules/known_fragment_names.py
deleted file mode 100644
index e572ce8..0000000
--- a/graphql/validation/rules/known_fragment_names.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-
-class KnownFragmentNames(ValidationRule):
-    def enter_FragmentSpread(self, node, key, parent, path, ancestors):
-        fragment_name = node.name.value
-        fragment = self.context.get_fragment(fragment_name)
-
-        if not fragment:
-            self.context.report_error(
-                GraphQLError(self.unknown_fragment_message(fragment_name), [node.name])
-            )
-
-    @staticmethod
-    def unknown_fragment_message(fragment_name):
-        return 'Unknown fragment "{}".'.format(fragment_name)
diff --git a/graphql/validation/rules/known_type_names.py b/graphql/validation/rules/known_type_names.py
deleted file mode 100644
index 4c68c27..0000000
--- a/graphql/validation/rules/known_type_names.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from ...error import GraphQLError
-from ...utils.quoted_or_list import quoted_or_list
-from ...utils.suggestion_list import suggestion_list
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import NamedType
-    from typing import Any
-
-
-def _unknown_type_message(type, suggested_types):
-    message = 'Unknown type "{}".'.format(type)
-    if suggested_types:
-        message += " Perhaps you meant {}?".format(quoted_or_list(suggested_types))
-
-    return message
-
-
-class KnownTypeNames(ValidationRule):
-    def enter_ObjectTypeDefinition(self, node, *args):
-        return False
-
-    def enter_InterfaceTypeDefinition(self, node, *args):
-        return False
-
-    def enter_UnionTypeDefinition(self, node, *args):
-        return False
-
-    def enter_InputObjectTypeDefinition(self, node, *args):
-        return False
-
-    def enter_NamedType(self, node, *args):
-        # type: (NamedType, *Any) -> None
-        schema = self.context.get_schema()
-        type_name = node.name.value
-        type = schema.get_type(type_name)
-
-        if not type:
-            self.context.report_error(
-                GraphQLError(
-                    _unknown_type_message(
-                        type_name,
-                        suggestion_list(type_name, list(schema.get_type_map().keys())),
-                    ),
-                    [node],
-                )
-            )
diff --git a/graphql/validation/rules/lone_anonymous_operation.py b/graphql/validation/rules/lone_anonymous_operation.py
deleted file mode 100644
index 51b1ae2..0000000
--- a/graphql/validation/rules/lone_anonymous_operation.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from ...error import GraphQLError
-from ...language import ast
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition
-    from typing import Any, List, Optional, Union
-
-
-class LoneAnonymousOperation(ValidationRule):
-    __slots__ = ("operation_count",)
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        self.operation_count = 0
-        super(LoneAnonymousOperation, self).__init__(context)
-
-    def enter_Document(self, node, key, parent, path, ancestors):
-        # type: (Document, Optional[Any], Optional[Any], List, List) -> None
-        self.operation_count = sum(
-            1
-            for definition in node.definitions
-            if isinstance(definition, ast.OperationDefinition)
-        )
-
-    def enter_OperationDefinition(
-        self,
-        node,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> None
-        if not node.name and self.operation_count > 1:
-            self.context.report_error(
-                GraphQLError(self.anonymous_operation_not_alone_message(), [node])
-            )
-
-    @staticmethod
-    def anonymous_operation_not_alone_message():
-        return "This anonymous operation must be the only defined operation."
diff --git a/graphql/validation/rules/no_fragment_cycles.py b/graphql/validation/rules/no_fragment_cycles.py
deleted file mode 100644
index 474bad9..0000000
--- a/graphql/validation/rules/no_fragment_cycles.py
+++ /dev/null
@@ -1,75 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition, FragmentSpread
-    from typing import List, Union, Dict, Set
-
-
-class NoFragmentCycles(ValidationRule):
-    __slots__ = "errors", "visited_frags", "spread_path", "spread_path_index_by_name"
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(NoFragmentCycles, self).__init__(context)
-        self.errors = []  # type: List[GraphQLError]
-        self.visited_frags = set()  # type: Set[str]
-        self.spread_path = []  # type: List[FragmentSpread]
-        self.spread_path_index_by_name = {}  # type: Dict[str, int]
-
-    def enter_OperationDefinition(
-        self,
-        node,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> bool
-        return False
-
-    def enter_FragmentDefinition(self, node, key, parent, path, ancestors):
-        if node.name.value not in self.visited_frags:
-            self.detect_cycle_recursive(node)
-        return False
-
-    def detect_cycle_recursive(self, fragment):
-        fragment_name = fragment.name.value
-        self.visited_frags.add(fragment_name)
-
-        spread_nodes = self.context.get_fragment_spreads(fragment.selection_set)
-        if not spread_nodes:
-            return
-
-        self.spread_path_index_by_name[fragment_name] = len(self.spread_path)
-
-        for spread_node in spread_nodes:
-            spread_name = spread_node.name.value
-            cycle_index = self.spread_path_index_by_name.get(spread_name)
-
-            if cycle_index is None:
-                self.spread_path.append(spread_node)
-                if spread_name not in self.visited_frags:
-                    spread_fragment = self.context.get_fragment(spread_name)
-                    if spread_fragment:
-                        self.detect_cycle_recursive(spread_fragment)
-                self.spread_path.pop()
-            else:
-                cycle_path = self.spread_path[cycle_index:]
-                self.context.report_error(
-                    GraphQLError(
-                        self.cycle_error_message(
-                            spread_name, [s.name.value for s in cycle_path]
-                        ),
-                        cycle_path + [spread_node],
-                    )
-                )
-
-        self.spread_path_index_by_name[fragment_name] = None
-
-    @staticmethod
-    def cycle_error_message(fragment_name, spread_names):
-        via = " via {}".format(", ".join(spread_names)) if spread_names else ""
-        return 'Cannot spread fragment "{}" within itself{}.'.format(fragment_name, via)
diff --git a/graphql/validation/rules/no_undefined_variables.py b/graphql/validation/rules/no_undefined_variables.py
deleted file mode 100644
index 59df5bb..0000000
--- a/graphql/validation/rules/no_undefined_variables.py
+++ /dev/null
@@ -1,63 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition
-    from typing import List, Union, Set
-
-
-class NoUndefinedVariables(ValidationRule):
-    __slots__ = ("defined_variable_names",)
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        self.defined_variable_names = set()  # type: Set[str]
-        super(NoUndefinedVariables, self).__init__(context)
-
-    @staticmethod
-    def undefined_var_message(var_name, op_name=None):
-        if op_name:
-            return 'Variable "${}" is not defined by operation "{}".'.format(
-                var_name, op_name
-            )
-        return 'Variable "${}" is not defined.'.format(var_name)
-
-    def enter_OperationDefinition(
-        self,
-        operation,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> None
-        self.defined_variable_names = set()
-
-    def leave_OperationDefinition(
-        self,
-        operation,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[str]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> None
-        usages = self.context.get_recursive_variable_usages(operation)
-
-        for variable_usage in usages:
-            node = variable_usage.node
-            var_name = node.name.value
-            if var_name not in self.defined_variable_names:
-                self.context.report_error(
-                    GraphQLError(
-                        self.undefined_var_message(
-                            var_name, operation.name and operation.name.value
-                        ),
-                        [node, operation],
-                    )
-                )
-
-    def enter_VariableDefinition(self, node, key, parent, path, ancestors):
-        self.defined_variable_names.add(node.variable.name.value)
diff --git a/graphql/validation/rules/no_unused_fragments.py b/graphql/validation/rules/no_unused_fragments.py
deleted file mode 100644
index 320a0c4..0000000
--- a/graphql/validation/rules/no_unused_fragments.py
+++ /dev/null
@@ -1,61 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition, FragmentDefinition
-    from typing import List, Union, Any, Optional
-
-
-class NoUnusedFragments(ValidationRule):
-    __slots__ = (
-        "fragment_definitions",
-        "operation_definitions",
-        "fragment_adjacencies",
-        "spread_names",
-    )
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(NoUnusedFragments, self).__init__(context)
-        self.operation_definitions = []  # type: List[OperationDefinition]
-        self.fragment_definitions = []  # type: List[FragmentDefinition]
-
-    def enter_OperationDefinition(
-        self,
-        node,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> bool
-        self.operation_definitions.append(node)
-        return False
-
-    def enter_FragmentDefinition(self, node, key, parent, path, ancestors):
-        self.fragment_definitions.append(node)
-        return False
-
-    def leave_Document(self, node, key, parent, path, ancestors):
-        # type: (Document, Optional[Any], Optional[Any], List, List) -> None
-        fragment_names_used = set()
-
-        for operation in self.operation_definitions:
-            fragments = self.context.get_recursively_referenced_fragments(operation)
-            for fragment in fragments:
-                fragment_names_used.add(fragment.name.value)
-
-        for fragment_definition in self.fragment_definitions:
-            if fragment_definition.name.value not in fragment_names_used:
-                self.context.report_error(
-                    GraphQLError(
-                        self.unused_fragment_message(fragment_definition.name.value),
-                        [fragment_definition],
-                    )
-                )
-
-    @staticmethod
-    def unused_fragment_message(fragment_name):
-        return 'Fragment "{}" is never used.'.format(fragment_name)
diff --git a/graphql/validation/rules/no_unused_variables.py b/graphql/validation/rules/no_unused_variables.py
deleted file mode 100644
index 49b81fa..0000000
--- a/graphql/validation/rules/no_unused_variables.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition, VariableDefinition
-    from typing import List, Union
-
-
-class NoUnusedVariables(ValidationRule):
-    __slots__ = "variable_definitions"
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        self.variable_definitions = []  # type: List[VariableDefinition]
-        super(NoUnusedVariables, self).__init__(context)
-
-    def enter_OperationDefinition(
-        self,
-        node,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> None
-        self.variable_definitions = []
-
-    def leave_OperationDefinition(
-        self,
-        operation,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[str]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> None
-        variable_name_used = set()
-        usages = self.context.get_recursive_variable_usages(operation)
-        op_name = operation.name and operation.name.value or None
-
-        for variable_usage in usages:
-            variable_name_used.add(variable_usage.node.name.value)
-
-        for variable_definition in self.variable_definitions:
-            if variable_definition.variable.name.value not in variable_name_used:
-                self.context.report_error(
-                    GraphQLError(
-                        self.unused_variable_message(
-                            variable_definition.variable.name.value, op_name
-                        ),
-                        [variable_definition],
-                    )
-                )
-
-    def enter_VariableDefinition(self, node, key, parent, path, ancestors):
-        self.variable_definitions.append(node)
-
-    @staticmethod
-    def unused_variable_message(variable_name, op_name):
-        if op_name:
-            return 'Variable "${}" is never used in operation "{}".'.format(
-                variable_name, op_name
-            )
-        return 'Variable "${}" is never used.'.format(variable_name)
diff --git a/graphql/validation/rules/overlapping_fields_can_be_merged.py b/graphql/validation/rules/overlapping_fields_can_be_merged.py
deleted file mode 100644
index f8b01e2..0000000
--- a/graphql/validation/rules/overlapping_fields_can_be_merged.py
+++ /dev/null
@@ -1,757 +0,0 @@
-import itertools
-from collections import OrderedDict
-
-from ...error import GraphQLError
-from ...language import ast
-from ...language.printer import print_ast
-from ...pyutils.pair_set import PairSet
-from ...type.definition import (
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    get_named_type,
-    is_leaf_type,
-)
-from ...utils.type_comparators import is_equal_type
-from ...utils.type_from_ast import type_from_ast
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import (
-        Node,
-        OperationDefinition,
-        Field,
-        Argument,
-        InlineFragment,
-        SelectionSet,
-    )
-    from ...type.definition import GraphQLField, GraphQLScalarType
-    from typing import List, Union, Any, Optional, Dict, Tuple
-
-
-class OverlappingFieldsCanBeMerged(ValidationRule):
-    __slots__ = ("_compared_fragments", "_cached_fields_and_fragment_names")
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(OverlappingFieldsCanBeMerged, self).__init__(context)
-        # A memoization for when two fragments are compared "between" each other for
-        # conflicts. Two fragments may be compared many times, so memoizing this can
-        # dramatically improve the performance of this validator.
-        self._compared_fragments = PairSet()
-
-        # A cache for the "field map" and list of fragment names found in any given
-        # selection set. Selection sets may be asked for this information multiple
-        # times, so this improves the performance of this validator.
-        self._cached_fields_and_fragment_names = (
-            {}
-        )  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-
-    def leave_SelectionSet(
-        self,
-        node,  # type: SelectionSet
-        key,  # type: str
-        parent,  # type: Union[Field, InlineFragment, OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        # Note: we validate on the reverse traversal so deeper conflicts will be
-        # caught first, for correct calculation of mutual exclusivity and for
-        # clearer error messages.
-        # field_map = _collect_field_asts_and_defs(
-        #     self.context,
-        #     self.context.get_parent_type(),
-        #     node
-        # )
-
-        # conflicts = _find_conflicts(self.context, False, field_map, self.compared_set)
-        conflicts = _find_conflicts_within_selection_set(
-            self.context,
-            self._cached_fields_and_fragment_names,
-            self._compared_fragments,
-            self.context.get_parent_type(),
-            node,
-        )
-
-        for (reason_name, reason), fields1, fields2 in conflicts:
-            self.context.report_error(
-                GraphQLError(
-                    self.fields_conflict_message(reason_name, reason),
-                    list(fields1) + list(fields2),
-                )
-            )
-
-    @staticmethod
-    def same_type(type1, type2):
-        return is_equal_type(type1, type2)
-        # return type1.is_same_type(type2)
-
-    @classmethod
-    def fields_conflict_message(cls, reason_name, reason):
-        return (
-            'Fields "{}" conflict because {}. '
-            "Use different aliases on the fields to fetch both if this was "
-            "intentional."
-        ).format(reason_name, cls.reason_message(reason))
-
-    @classmethod
-    def reason_message(cls, reason):
-        if isinstance(reason, list):
-            return " and ".join(
-                'subfields "{}" conflict because {}'.format(
-                    reason_name, cls.reason_message(sub_reason)
-                )
-                for reason_name, sub_reason in reason
-            )
-
-        return reason
-
-
-# Algorithm:
-#
-#  Conflicts occur when two fields exist in a query which will produce the same
-#  response name, but represent differing values, thus creating a conflict.
-#  The algorithm below finds all conflicts via making a series of comparisons
-#  between fields. In order to compare as few fields as possible, this makes
-#  a series of comparisons "within" sets of fields and "between" sets of fields.
-#
-#  Given any selection set, a collection produces both a set of fields by
-#  also including all inline fragments, as well as a list of fragments
-#  referenced by fragment spreads.
-#
-#  A) Each selection set represented in the document first compares "within" its
-#  collected set of fields, finding any conflicts between every pair of
-#  overlapping fields.
-#  Note: This is the only time that a the fields "within" a set are compared
-#  to each other. After this only fields "between" sets are compared.
-#
-#  B) Also, if any fragment is referenced in a selection set, then a
-#  comparison is made "between" the original set of fields and the
-#  referenced fragment.
-#
-#  C) Also, if multiple fragments are referenced, then comparisons
-#  are made "between" each referenced fragment.
-#
-#  D) When comparing "between" a set of fields and a referenced fragment, first
-#  a comparison is made between each field in the original set of fields and
-#  each field in the the referenced set of fields.
-#
-#  E) Also, if any fragment is referenced in the referenced selection set,
-#  then a comparison is made "between" the original set of fields and the
-#  referenced fragment (recursively referring to step D).
-#
-#  F) When comparing "between" two fragments, first a comparison is made between
-#  each field in the first referenced set of fields and each field in the the
-#  second referenced set of fields.
-#
-#  G) Also, any fragments referenced by the first must be compared to the
-#  second, and any fragments referenced by the second must be compared to the
-#  first (recursively referring to step F).
-#
-#  H) When comparing two fields, if both have selection sets, then a comparison
-#  is made "between" both selection sets, first comparing the set of fields in
-#  the first selection set with the set of fields in the second.
-#
-#  I) Also, if any fragment is referenced in either selection set, then a
-#  comparison is made "between" the other set of fields and the
-#  referenced fragment.
-#
-#  J) Also, if two fragments are referenced in both selection sets, then a
-#  comparison is made "between" the two fragments.
-
-
-def _find_conflicts_within_selection_set(
-    context,  # type: ValidationContext
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    compared_fragments,  # type: PairSet
-    parent_type,  # type: Union[GraphQLInterfaceType, GraphQLObjectType, None]
-    selection_set,  # type: SelectionSet
-):
-    # type: (...) ->  List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    """Find all conflicts found "within" a selection set, including those found via spreading in fragments.
-
-       Called when visiting each SelectionSet in the GraphQL Document.
-    """
-    conflicts = []  # type: List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    field_map, fragment_names = _get_fields_and_fragments_names(
-        context, cached_fields_and_fragment_names, parent_type, selection_set
-    )
-
-    # (A) Find all conflicts "within" the fields of this selection set.
-    # Note: this is the *only place* `collect_conflicts_within` is called.
-    _collect_conflicts_within(
-        context,
-        conflicts,
-        cached_fields_and_fragment_names,
-        compared_fragments,
-        field_map,
-    )
-
-    # (B) Then collect conflicts between these fields and those represented by
-    # each spread fragment name found.
-    for i, fragment_name in enumerate(fragment_names):
-        _collect_conflicts_between_fields_and_fragment(
-            context,
-            conflicts,
-            cached_fields_and_fragment_names,
-            compared_fragments,
-            False,
-            field_map,
-            fragment_name,
-        )
-
-        # (C) Then compare this fragment with all other fragments found in this
-        # selection set to collect conflicts within fragments spread together.
-        # This compares each item in the list of fragment names to every other item
-        # in that same list (except for itself).
-        for other_fragment_name in fragment_names[i + 1 :]:
-            _collect_conflicts_between_fragments(
-                context,
-                conflicts,
-                cached_fields_and_fragment_names,
-                compared_fragments,
-                False,
-                fragment_name,
-                other_fragment_name,
-            )
-
-    return conflicts
-
-
-def _collect_conflicts_between_fields_and_fragment(
-    context,  # type: ValidationContext
-    conflicts,  # type: List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    compared_fragments,  # type: PairSet
-    are_mutually_exclusive,  # type: bool
-    field_map,  # type: Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]]
-    fragment_name,  # type: str
-):
-
-    fragment = context.get_fragment(fragment_name)
-
-    if not fragment:
-        return None
-
-    field_map2, fragment_names2 = _get_referenced_fields_and_fragment_names(
-        context, cached_fields_and_fragment_names, fragment
-    )
-
-    # (D) First collect any conflicts between the provided collection of fields
-    # and the collection of fields represented by the given fragment.
-    _collect_conflicts_between(
-        context,
-        conflicts,
-        cached_fields_and_fragment_names,
-        compared_fragments,
-        are_mutually_exclusive,
-        field_map,
-        field_map2,
-    )
-
-    # (E) Then collect any conflicts between the provided collection of fields
-    # and any fragment names found in the given fragment.
-    for fragment_name2 in fragment_names2:
-        _collect_conflicts_between_fields_and_fragment(
-            context,
-            conflicts,
-            cached_fields_and_fragment_names,
-            compared_fragments,
-            are_mutually_exclusive,
-            field_map,
-            fragment_name2,
-        )
-
-
-# Collect all conflicts found between two fragments, including via spreading in
-# any nested fragments
-def _collect_conflicts_between_fragments(
-    context,  # type: ValidationContext
-    conflicts,  # type: List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    compared_fragments,  # type: PairSet
-    are_mutually_exclusive,  # type: bool
-    fragment_name1,  # type: str
-    fragment_name2,  # type: str
-):
-
-    fragment1 = context.get_fragment(fragment_name1)
-    fragment2 = context.get_fragment(fragment_name2)
-
-    if not fragment1 or not fragment2:
-        return None
-
-    # No need to compare a fragment to itself.
-    if fragment1 == fragment2:
-        return None
-
-    # Memoize so two fragments are not compared for conflicts more than once.
-    if compared_fragments.has(fragment_name1, fragment_name2, are_mutually_exclusive):
-        return None
-
-    compared_fragments.add(fragment_name1, fragment_name2, are_mutually_exclusive)
-
-    field_map1, fragment_names1 = _get_referenced_fields_and_fragment_names(
-        context, cached_fields_and_fragment_names, fragment1
-    )
-
-    field_map2, fragment_names2 = _get_referenced_fields_and_fragment_names(
-        context, cached_fields_and_fragment_names, fragment2
-    )
-
-    # (F) First, collect all conflicts between these two collections of fields
-    # (not including any nested fragments)
-    _collect_conflicts_between(
-        context,
-        conflicts,
-        cached_fields_and_fragment_names,
-        compared_fragments,
-        are_mutually_exclusive,
-        field_map1,
-        field_map2,
-    )
-
-    # (G) Then collect conflicts between the first fragment and any nested
-    # fragments spread in the second fragment.
-    for _fragment_name2 in fragment_names2:
-        _collect_conflicts_between_fragments(
-            context,
-            conflicts,
-            cached_fields_and_fragment_names,
-            compared_fragments,
-            are_mutually_exclusive,
-            fragment_name1,
-            _fragment_name2,
-        )
-
-    # (G) Then collect conflicts between the second fragment and any nested
-    # fragments spread in the first fragment.
-    for _fragment_name1 in fragment_names1:
-        _collect_conflicts_between_fragments(
-            context,
-            conflicts,
-            cached_fields_and_fragment_names,
-            compared_fragments,
-            are_mutually_exclusive,
-            _fragment_name1,
-            fragment_name2,
-        )
-
-    conflicts,  # type: List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    are_mutually_exclusive,  # type: bool
-
-
-def _find_conflicts_between_sub_selection_sets(
-    context,  # type: ValidationContext
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    compared_fragments,  # type: PairSet
-    are_mutually_exclusive,  # type: bool
-    parent_type1,  # type: Union[GraphQLInterfaceType, GraphQLObjectType, None]
-    selection_set1,  # type: SelectionSet
-    parent_type2,  # type: Union[GraphQLInterfaceType, GraphQLObjectType, None]
-    selection_set2,  # type: SelectionSet
-):
-    # type: (...) ->  List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    """Find all conflicts found between two selection sets.
-
-       Includes those found via spreading in fragments. Called when determining if conflicts exist
-       between the sub-fields of two overlapping fields.
-    """
-    conflicts = []  # type: List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-
-    field_map1, fragment_names1 = _get_fields_and_fragments_names(
-        context, cached_fields_and_fragment_names, parent_type1, selection_set1
-    )
-
-    field_map2, fragment_names2 = _get_fields_and_fragments_names(
-        context, cached_fields_and_fragment_names, parent_type2, selection_set2
-    )
-
-    # (H) First, collect all conflicts between these two collections of field.
-    _collect_conflicts_between(
-        context,
-        conflicts,
-        cached_fields_and_fragment_names,
-        compared_fragments,
-        are_mutually_exclusive,
-        field_map1,
-        field_map2,
-    )
-
-    # (I) Then collect conflicts between the first collection of fields and
-    # those referenced by each fragment name associated with the second.
-    for fragment_name2 in fragment_names2:
-        _collect_conflicts_between_fields_and_fragment(
-            context,
-            conflicts,
-            cached_fields_and_fragment_names,
-            compared_fragments,
-            are_mutually_exclusive,
-            field_map1,
-            fragment_name2,
-        )
-
-    # (I) Then collect conflicts between the second collection of fields and
-    #  those referenced by each fragment name associated with the first.
-    for fragment_name1 in fragment_names1:
-        _collect_conflicts_between_fields_and_fragment(
-            context,
-            conflicts,
-            cached_fields_and_fragment_names,
-            compared_fragments,
-            are_mutually_exclusive,
-            field_map2,
-            fragment_name1,
-        )
-
-    # (J) Also collect conflicts between any fragment names by the first and
-    # fragment names by the second. This compares each item in the first set of
-    # names to each item in the second set of names.
-    for fragment_name1 in fragment_names1:
-        for fragment_name2 in fragment_names2:
-            _collect_conflicts_between_fragments(
-                context,
-                conflicts,
-                cached_fields_and_fragment_names,
-                compared_fragments,
-                are_mutually_exclusive,
-                fragment_name1,
-                fragment_name2,
-            )
-
-    return conflicts
-
-
-def _collect_conflicts_within(
-    context,  # type: ValidationContext
-    conflicts,  # type: List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    compared_fragments,  # type: PairSet
-    field_map,  # type: Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]]
-):
-    # type: (...) -> None
-    """Collect all Conflicts "within" one collection of fields."""
-
-    # field map is a keyed collection, where each key represents a response
-    # name and the value at that key is a list of all fields which provide that
-    # response name. For every response name, if there are multiple fields, they
-    # must be compared to find a potential conflict.
-    for response_name, fields in list(field_map.items()):
-        # This compares every field in the list to every other field in this list
-        # (except to itself). If the list only has one item, nothing needs to
-        # be compared.
-        for i, field in enumerate(fields):
-            for other_field in fields[i + 1 :]:
-                # within one collection is never mutually exclusive
-                conflict = _find_conflict(
-                    context,
-                    cached_fields_and_fragment_names,
-                    compared_fragments,
-                    False,
-                    response_name,
-                    field,
-                    other_field,
-                )
-                if conflict:
-                    conflicts.append(conflict)
-
-
-def _collect_conflicts_between(
-    context,  # type: ValidationContext
-    conflicts,  # type: List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    compared_fragments,  # type: PairSet
-    parent_fields_are_mutually_exclusive,  # type: bool
-    field_map1,  # type: Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]]
-    field_map2,  # type: Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]]
-):
-    # type: (...) -> None
-    """Collect all Conflicts between two collections of fields.
-
-       This is similar to, but different from the `collect_conflicts_within` function above. This check assumes that
-       `collect_conflicts_within` has already been called on each provided collection of fields.
-       This is true because this validator traverses each individual selection set.
-    """
-    # A field map is a keyed collection, where each key represents a response
-    # name and the value at that key is a list of all fields which provide that
-    # response name. For any response name which appears in both provided field
-    # maps, each field from the first field map must be compared to every field
-    # in the second field map to find potential conflicts.
-    for response_name, fields1 in list(field_map1.items()):
-        fields2 = field_map2.get(response_name)
-
-        if fields2:
-            for field1 in fields1:
-                for field2 in fields2:
-                    conflict = _find_conflict(
-                        context,
-                        cached_fields_and_fragment_names,
-                        compared_fragments,
-                        parent_fields_are_mutually_exclusive,
-                        response_name,
-                        field1,
-                        field2,
-                    )
-
-                    if conflict:
-                        conflicts.append(conflict)
-
-
-def _find_conflict(
-    context,  # type: ValidationContext
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    compared_fragments,  # type: PairSet
-    parent_fields_are_mutually_exclusive,  # type: bool
-    response_name,  # type: str
-    field1,  # type: Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]
-    field2,  # type: Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]
-):
-    # type: (...) -> Optional[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    """Determines if there is a conflict between two particular fields."""
-    parent_type1, ast1, def1 = field1
-    parent_type2, ast2, def2 = field2
-
-    # If it is known that two fields could not possibly apply at the same
-    # time, due to the parent types, then it is safe to permit them to diverge
-    # in aliased field or arguments used as they will not present any ambiguity
-    # by differing.
-    # It is known that two parent types could never overlap if they are
-    # different Object types. Interface or Union types might overlap - if not
-    # in the current state of the schema, then perhaps in some future version,
-    # thus may not safely diverge.
-
-    are_mutually_exclusive = parent_fields_are_mutually_exclusive or (
-        parent_type1 != parent_type2
-        and isinstance(parent_type1, GraphQLObjectType)
-        and isinstance(parent_type2, GraphQLObjectType)
-    )
-
-    # The return type for each field.
-    type1 = def1 and def1.type
-    type2 = def2 and def2.type
-
-    if not are_mutually_exclusive:
-        # Two aliases must refer to the same field.
-        name1 = ast1.name.value
-        name2 = ast2.name.value
-
-        if name1 != name2:
-            return (
-                (response_name, "{} and {} are different fields".format(name1, name2)),
-                [ast1],
-                [ast2],
-            )
-
-        # Two field calls must have the same arguments.
-        if not _same_arguments(ast1.arguments, ast2.arguments):
-            return ((response_name, "they have differing arguments"), [ast1], [ast2])
-
-    if type1 and type2 and do_types_conflict(type1, type2):
-        return (
-            (
-                response_name,
-                "they return conflicting types {} and {}".format(type1, type2),
-            ),
-            [ast1],
-            [ast2],
-        )
-
-    #  Collect and compare sub-fields. Use the same "visited fragment names" list
-    # for both collections so fields in a fragment reference are never
-    # compared to themselves.
-    selection_set1 = ast1.selection_set
-    selection_set2 = ast2.selection_set
-
-    if selection_set1 and selection_set2:
-        conflicts = _find_conflicts_between_sub_selection_sets(  # type: ignore
-            context,
-            cached_fields_and_fragment_names,
-            compared_fragments,
-            are_mutually_exclusive,
-            get_named_type(type1),  # type: ignore
-            selection_set1,
-            get_named_type(type2),  # type: ignore
-            selection_set2,
-        )
-
-        return _subfield_conflicts(conflicts, response_name, ast1, ast2)
-
-    return None
-
-
-def _get_fields_and_fragments_names(
-    context,  # type: ValidationContext
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    parent_type,  # type: Union[GraphQLInterfaceType, GraphQLObjectType, None]
-    selection_set,  # type: SelectionSet
-):
-    # type: (...) -> Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]
-    cached = cached_fields_and_fragment_names.get(selection_set)
-
-    if not cached:
-        ast_and_defs = (
-            OrderedDict()
-        )  # type: Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]]
-        fragment_names = OrderedDict()  # type: Dict[str, bool]
-        _collect_fields_and_fragment_names(
-            context, parent_type, selection_set, ast_and_defs, fragment_names
-        )
-        cached = (ast_and_defs, list(fragment_names.keys()))
-        cached_fields_and_fragment_names[selection_set] = cached
-
-    return cached
-
-
-def _get_referenced_fields_and_fragment_names(
-    context,  # ValidationContext
-    cached_fields_and_fragment_names,  # type: Dict[SelectionSet, Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]]
-    fragment,  # type: InlineFragment
-):
-    # type: (...) -> Tuple[Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]], List[str]]
-    """Given a reference to a fragment, return the represented collection of fields as well as a list of
-    nested fragment names referenced via fragment spreads."""
-
-    # Short-circuit building a type from the AST if possible.
-    cached = cached_fields_and_fragment_names.get(fragment.selection_set)
-
-    if cached:
-        return cached
-
-    fragment_type = type_from_ast(  # type: ignore
-        context.get_schema(), fragment.type_condition
-    )
-
-    return _get_fields_and_fragments_names(  # type: ignore
-        context, cached_fields_and_fragment_names, fragment_type, fragment.selection_set
-    )
-
-
-def _collect_fields_and_fragment_names(
-    context,  # type: ValidationContext
-    parent_type,  # type: Union[GraphQLInterfaceType, GraphQLObjectType, None]
-    selection_set,  # type: SelectionSet
-    ast_and_defs,  # type: Dict[str, List[Tuple[Union[GraphQLInterfaceType, GraphQLObjectType, None], Field, GraphQLField]]]
-    fragment_names,  # type: Dict[str, bool]
-):
-    # type: (...) -> None
-
-    for selection in selection_set.selections:
-        if isinstance(selection, ast.Field):
-            field_name = selection.name.value
-            if isinstance(parent_type, (GraphQLObjectType, GraphQLInterfaceType)):
-                field_def = parent_type.fields.get(field_name)
-            else:
-                field_def = None
-
-            response_name = selection.alias.value if selection.alias else field_name
-
-            if not ast_and_defs.get(response_name):
-                ast_and_defs[response_name] = []  # type: ignore
-
-            ast_and_defs[response_name].append((parent_type, selection, field_def))
-
-        elif isinstance(selection, ast.FragmentSpread):
-            fragment_names[selection.name.value] = True
-        elif isinstance(selection, ast.InlineFragment):
-            type_condition = selection.type_condition
-            if type_condition:
-                inline_fragment_type = type_from_ast(  # type: ignore
-                    context.get_schema(), selection.type_condition
-                )
-            else:
-                inline_fragment_type = parent_type  # type: ignore
-
-            _collect_fields_and_fragment_names(  # type: ignore
-                context,
-                inline_fragment_type,
-                selection.selection_set,
-                ast_and_defs,
-                fragment_names,
-            )
-
-
-def _subfield_conflicts(
-    conflicts,  # type: List[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    response_name,  # type: str
-    ast1,  # type: Node
-    ast2,  # type: Node
-):
-    # type: (...) -> Optional[Tuple[Tuple[str, str], List[Node], List[Node]]]
-    """Given a series of Conflicts which occurred between two sub-fields, generate a single Conflict."""
-    if conflicts:
-        return (  # type: ignore
-            (response_name, [conflict[0] for conflict in conflicts]),
-            tuple(itertools.chain([ast1], *[conflict[1] for conflict in conflicts])),
-            tuple(itertools.chain([ast2], *[conflict[2] for conflict in conflicts])),
-        )
-    return None
-
-
-def do_types_conflict(type1, type2):
-    # type: (GraphQLScalarType, GraphQLScalarType) -> bool
-    if isinstance(type1, GraphQLList):
-        if isinstance(type2, GraphQLList):
-            return do_types_conflict(type1.of_type, type2.of_type)
-        return True
-
-    if isinstance(type2, GraphQLList):
-        if isinstance(type1, GraphQLList):
-            return do_types_conflict(type1.of_type, type2.of_type)
-        return True
-
-    if isinstance(type1, GraphQLNonNull):
-        if isinstance(type2, GraphQLNonNull):
-            return do_types_conflict(type1.of_type, type2.of_type)
-        return True
-
-    if isinstance(type2, GraphQLNonNull):
-        if isinstance(type1, GraphQLNonNull):
-            return do_types_conflict(type1.of_type, type2.of_type)
-        return True
-
-    if is_leaf_type(type1) or is_leaf_type(type2):
-        return type1 != type2
-
-    return False
-
-
-def _same_value(value1, value2):
-    # type: (Optional[Node], Optional[Node]) -> bool
-    if not value1 and not value2:
-        return True
-    if not value1 or not value2:
-        return False
-    return print_ast(value1) == print_ast(value2)
-
-
-def _same_arguments(arguments1, arguments2):
-    # type: (Optional[List[Argument]], Optional[List[Argument]]) -> bool
-    # Check to see if they are empty arguments or nones. If they are, we can
-    # bail out early.
-    if not arguments1 and not arguments2:
-        return True
-
-    if not arguments1:
-        return False
-
-    if not arguments2:
-        return False
-
-    if len(arguments1) != len(arguments2):
-        return False
-
-    arguments2_values_to_arg = {a.name.value: a for a in arguments2}
-
-    for argument1 in arguments1:
-        argument2 = arguments2_values_to_arg.get(argument1.name.value)
-        if not argument2:
-            return False
-
-        if not _same_value(argument1.value, argument2.value):
-            return False
-
-    return True
diff --git a/graphql/validation/rules/possible_fragment_spreads.py b/graphql/validation/rules/possible_fragment_spreads.py
deleted file mode 100644
index 460d919..0000000
--- a/graphql/validation/rules/possible_fragment_spreads.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from ...error import GraphQLError
-from ...utils.type_comparators import do_types_overlap
-from ...utils.type_from_ast import type_from_ast
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import Field, InlineFragment
-    from typing import Any, List, Union
-
-
-class PossibleFragmentSpreads(ValidationRule):
-    def enter_InlineFragment(
-        self,
-        node,  # type: InlineFragment
-        key,  # type: int
-        parent,  # type: List[Union[Field, InlineFragment]]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        frag_type = self.context.get_type()
-        parent_type = self.context.get_parent_type()
-        schema = self.context.get_schema()
-        if (
-            frag_type
-            and parent_type
-            and not do_types_overlap(schema, frag_type, parent_type)  # type: ignore
-        ):
-            self.context.report_error(
-                GraphQLError(
-                    self.type_incompatible_anon_spread_message(parent_type, frag_type),
-                    [node],
-                )
-            )
-
-    def enter_FragmentSpread(self, node, key, parent, path, ancestors):
-        frag_name = node.name.value
-        frag_type = self.get_fragment_type(self.context, frag_name)
-        parent_type = self.context.get_parent_type()
-        schema = self.context.get_schema()
-        if (
-            frag_type
-            and parent_type
-            and not do_types_overlap(schema, frag_type, parent_type)
-        ):
-            self.context.report_error(
-                GraphQLError(
-                    self.type_incompatible_spread_message(
-                        frag_name, parent_type, frag_type
-                    ),
-                    [node],
-                )
-            )
-
-    @staticmethod
-    def get_fragment_type(context, name):
-        frag = context.get_fragment(name)
-        return frag and type_from_ast(context.get_schema(), frag.type_condition)
-
-    @staticmethod
-    def type_incompatible_spread_message(frag_name, parent_type, frag_type):
-        return "Fragment {} cannot be spread here as objects of type {} can never be of type {}".format(
-            frag_name, parent_type, frag_type
-        )
-
-    @staticmethod
-    def type_incompatible_anon_spread_message(parent_type, frag_type):
-        return "Fragment cannot be spread here as objects of type {} can never be of type {}".format(
-            parent_type, frag_type
-        )
diff --git a/graphql/validation/rules/provided_non_null_arguments.py b/graphql/validation/rules/provided_non_null_arguments.py
deleted file mode 100644
index bfdbabe..0000000
--- a/graphql/validation/rules/provided_non_null_arguments.py
+++ /dev/null
@@ -1,71 +0,0 @@
-from ...error import GraphQLError
-from ...type.definition import GraphQLNonNull
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import Field, InlineFragment
-    from typing import Any, List, Optional, Union
-
-
-class ProvidedNonNullArguments(ValidationRule):
-    def leave_Field(
-        self,
-        node,  # type: Field
-        key,  # type: int
-        parent,  # type: Union[List[Union[Field, InlineFragment]], List[Field]]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> Optional[Any]
-        field_def = self.context.get_field_def()
-        if not field_def:
-            return False
-
-        arg_asts = node.arguments or []
-        arg_ast_map = {arg.name.value: arg for arg in arg_asts}
-
-        for arg_name, arg_def in field_def.args.items():
-            arg_ast = arg_ast_map.get(arg_name, None)
-            if not arg_ast and isinstance(arg_def.type, GraphQLNonNull):
-                self.context.report_error(
-                    GraphQLError(
-                        self.missing_field_arg_message(
-                            node.name.value, arg_name, arg_def.type
-                        ),
-                        [node],
-                    )
-                )
-        return None
-
-    def leave_Directive(self, node, key, parent, path, ancestors):
-        directive_def = self.context.get_directive()
-        if not directive_def:
-            return False
-
-        arg_asts = node.arguments or []
-        arg_ast_map = {arg.name.value: arg for arg in arg_asts}
-
-        for arg_name, arg_def in directive_def.args.items():
-            arg_ast = arg_ast_map.get(arg_name, None)
-            if not arg_ast and isinstance(arg_def.type, GraphQLNonNull):
-                self.context.report_error(
-                    GraphQLError(
-                        self.missing_directive_arg_message(
-                            node.name.value, arg_name, arg_def.type
-                        ),
-                        [node],
-                    )
-                )
-
-    @staticmethod
-    def missing_field_arg_message(name, arg_name, type):
-        return 'Field "{}" argument "{}" of type "{}" is required but not provided.'.format(
-            name, arg_name, type
-        )
-
-    @staticmethod
-    def missing_directive_arg_message(name, arg_name, type):
-        return 'Directive "{}" argument "{}" of type "{}" is required but not provided.'.format(
-            name, arg_name, type
-        )
diff --git a/graphql/validation/rules/scalar_leafs.py b/graphql/validation/rules/scalar_leafs.py
deleted file mode 100644
index a36c4e8..0000000
--- a/graphql/validation/rules/scalar_leafs.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from ...error import GraphQLError
-from ...type.definition import get_named_type, is_leaf_type
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ...language.ast import Field, InlineFragment
-    from typing import Any, List, Union
-
-
-class ScalarLeafs(ValidationRule):
-    def enter_Field(
-        self,
-        node,  # type: Field
-        key,  # type: int
-        parent,  # type: Union[List[Union[Field, InlineFragment]], List[Field]]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        type = self.context.get_type()
-
-        if not type:
-            return
-
-        if is_leaf_type(get_named_type(type)):
-            if node.selection_set:
-                self.context.report_error(
-                    GraphQLError(
-                        self.no_subselection_allowed_message(node.name.value, type),
-                        [node.selection_set],
-                    )
-                )
-
-        elif not node.selection_set:
-            self.context.report_error(
-                GraphQLError(
-                    self.required_subselection_message(node.name.value, type), [node]
-                )
-            )
-
-    @staticmethod
-    def no_subselection_allowed_message(field, type):
-        return 'Field "{}" of type "{}" must not have a sub selection.'.format(
-            field, type
-        )
-
-    @staticmethod
-    def required_subselection_message(field, type):
-        return 'Field "{}" of type "{}" must have a sub selection.'.format(field, type)
diff --git a/graphql/validation/rules/unique_argument_names.py b/graphql/validation/rules/unique_argument_names.py
deleted file mode 100644
index 4f7cefc..0000000
--- a/graphql/validation/rules/unique_argument_names.py
+++ /dev/null
@@ -1,57 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Field, InlineFragment, Argument, Name
-    from typing import Any, List, Union, Dict
-
-
-class UniqueArgumentNames(ValidationRule):
-    __slots__ = ("known_arg_names",)
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(UniqueArgumentNames, self).__init__(context)
-        self.known_arg_names = {}  # type: Dict[str, Name]
-
-    def enter_Field(
-        self,
-        node,  # type: Field
-        key,  # type: int
-        parent,  # type: Union[List[Union[Field, InlineFragment]], List[Field]]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        self.known_arg_names = {}
-
-    def enter_Directive(self, node, key, parent, path, ancestors):
-        self.known_arg_names = {}
-
-    def enter_Argument(
-        self,
-        node,  # type: Argument
-        key,  # type: int
-        parent,  # type: List[Argument]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> bool
-        arg_name = node.name.value
-
-        if arg_name in self.known_arg_names:
-            self.context.report_error(
-                GraphQLError(
-                    self.duplicate_arg_message(arg_name),
-                    [self.known_arg_names[arg_name], node.name],
-                )
-            )
-        else:
-            self.known_arg_names[arg_name] = node.name
-        return False
-
-    @staticmethod
-    def duplicate_arg_message(field):
-        return 'There can only be one argument named "{}".'.format(field)
diff --git a/graphql/validation/rules/unique_fragment_names.py b/graphql/validation/rules/unique_fragment_names.py
deleted file mode 100644
index aa7fa5b..0000000
--- a/graphql/validation/rules/unique_fragment_names.py
+++ /dev/null
@@ -1,45 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition, Name
-    from typing import List, Union, Dict
-
-
-class UniqueFragmentNames(ValidationRule):
-    __slots__ = ("known_fragment_names",)
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(UniqueFragmentNames, self).__init__(context)
-        self.known_fragment_names = {}  # type: Dict[str, Name]
-
-    def enter_OperationDefinition(
-        self,
-        node,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> bool
-        return False
-
-    def enter_FragmentDefinition(self, node, key, parent, path, ancestors):
-        fragment_name = node.name.value
-        if fragment_name in self.known_fragment_names:
-            self.context.report_error(
-                GraphQLError(
-                    self.duplicate_fragment_name_message(fragment_name),
-                    [self.known_fragment_names[fragment_name], node.name],
-                )
-            )
-        else:
-            self.known_fragment_names[fragment_name] = node.name
-        return False
-
-    @staticmethod
-    def duplicate_fragment_name_message(field):
-        return 'There can only be one fragment named "{}".'.format(field)
diff --git a/graphql/validation/rules/unique_input_field_names.py b/graphql/validation/rules/unique_input_field_names.py
deleted file mode 100644
index b0e6407..0000000
--- a/graphql/validation/rules/unique_input_field_names.py
+++ /dev/null
@@ -1,66 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Argument, ObjectValue, ObjectField, Name
-    from typing import Any, List, Union, Dict
-
-
-class UniqueInputFieldNames(ValidationRule):
-    __slots__ = "known_names", "known_names_stack"
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(UniqueInputFieldNames, self).__init__(context)
-        self.known_names = {}  # type: Dict[str, Name]
-        self.known_names_stack = []  # type: List[Dict[str, Name]]
-
-    def enter_ObjectValue(
-        self,
-        node,  # type: ObjectValue
-        key,  # type: str
-        parent,  # type: Argument
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        self.known_names_stack.append(self.known_names)
-        self.known_names = {}
-
-    def leave_ObjectValue(
-        self,
-        node,  # type: ObjectValue
-        key,  # type: str
-        parent,  # type: Argument
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> None
-        self.known_names = self.known_names_stack.pop()
-
-    def enter_ObjectField(
-        self,
-        node,  # type: ObjectField
-        key,  # type: int
-        parent,  # type: List[ObjectField]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Any]
-    ):
-        # type: (...) -> bool
-        field_name = node.name.value
-        if field_name in self.known_names:
-            self.context.report_error(
-                GraphQLError(
-                    self.duplicate_input_field_message(field_name),
-                    [self.known_names[field_name], node.name],
-                )
-            )
-        else:
-            self.known_names[field_name] = node.name
-        return False
-
-    @staticmethod
-    def duplicate_input_field_message(field_name):
-        return 'There can only be one input field named "{}".'.format(field_name)
diff --git a/graphql/validation/rules/unique_operation_names.py b/graphql/validation/rules/unique_operation_names.py
deleted file mode 100644
index f754659..0000000
--- a/graphql/validation/rules/unique_operation_names.py
+++ /dev/null
@@ -1,48 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition, Name
-    from typing import Any, List, Optional, Union, Dict
-
-
-class UniqueOperationNames(ValidationRule):
-    __slots__ = ("known_operation_names",)
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(UniqueOperationNames, self).__init__(context)
-        self.known_operation_names = {}  # type: Dict[str, Name]
-
-    def enter_OperationDefinition(
-        self,
-        node,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> Optional[Any]
-        operation_name = node.name
-        if not operation_name:
-            return None
-
-        if operation_name.value in self.known_operation_names:
-            self.context.report_error(
-                GraphQLError(
-                    self.duplicate_operation_name_message(operation_name.value),
-                    [self.known_operation_names[operation_name.value], operation_name],
-                )
-            )
-        else:
-            self.known_operation_names[operation_name.value] = operation_name
-        return False
-
-    def enter_FragmentDefinition(self, node, key, parent, path, ancestors):
-        return False
-
-    @staticmethod
-    def duplicate_operation_name_message(operation_name):
-        return 'There can only be one operation named "{}".'.format(operation_name)
diff --git a/graphql/validation/rules/unique_variable_names.py b/graphql/validation/rules/unique_variable_names.py
deleted file mode 100644
index 6d7bfcb..0000000
--- a/graphql/validation/rules/unique_variable_names.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from ...error import GraphQLError
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition
-    from typing import List, Union, Dict
-
-
-class UniqueVariableNames(ValidationRule):
-    __slots__ = ("known_variable_names",)
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(UniqueVariableNames, self).__init__(context)
-        self.known_variable_names = {}  # type: Dict[str, str]
-
-    def enter_OperationDefinition(
-        self,
-        node,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> None
-        self.known_variable_names = {}
-
-    def enter_VariableDefinition(self, node, key, parent, path, ancestors):
-        variable_name = node.variable.name.value
-        if variable_name in self.known_variable_names:
-            self.context.report_error(
-                GraphQLError(
-                    self.duplicate_variable_message(variable_name),
-                    [self.known_variable_names[variable_name], node.variable.name],
-                )
-            )
-        else:
-            self.known_variable_names[variable_name] = node.variable.name
-
-    @staticmethod
-    def duplicate_variable_message(operation_name):
-        return 'There can be only one variable named "{}".'.format(operation_name)
diff --git a/graphql/validation/rules/variables_are_input_types.py b/graphql/validation/rules/variables_are_input_types.py
deleted file mode 100644
index 49397ad..0000000
--- a/graphql/validation/rules/variables_are_input_types.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from ...error import GraphQLError
-from ...language.printer import print_ast
-from ...type.definition import is_input_type
-from ...utils.type_from_ast import type_from_ast
-from .base import ValidationRule
-
-
-class VariablesAreInputTypes(ValidationRule):
-    def enter_VariableDefinition(self, node, key, parent, path, ancestors):
-        type = type_from_ast(self.context.get_schema(), node.type)
-
-        if type and not is_input_type(type):
-            self.context.report_error(
-                GraphQLError(
-                    self.non_input_type_on_variable_message(
-                        node.variable.name.value, print_ast(node.type)
-                    ),
-                    [node.type],
-                )
-            )
-
-    @staticmethod
-    def non_input_type_on_variable_message(variable_name, type_name):
-        return 'Variable "${}" cannot be non-input type "{}".'.format(
-            variable_name, type_name
-        )
diff --git a/graphql/validation/rules/variables_in_allowed_position.py b/graphql/validation/rules/variables_in_allowed_position.py
deleted file mode 100644
index 8a0209d..0000000
--- a/graphql/validation/rules/variables_in_allowed_position.py
+++ /dev/null
@@ -1,89 +0,0 @@
-from ...error import GraphQLError
-from ...type.definition import GraphQLNonNull
-from ...utils.type_comparators import is_type_sub_type_of
-from ...utils.type_from_ast import type_from_ast
-from .base import ValidationRule
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from ..validation import ValidationContext
-    from ...language.ast import Document, OperationDefinition, VariableDefinition
-    from typing import List, Union, Dict, Any
-
-
-class VariablesInAllowedPosition(ValidationRule):
-    __slots__ = "var_def_map"
-
-    def __init__(self, context):
-        # type: (ValidationContext) -> None
-        super(VariablesInAllowedPosition, self).__init__(context)
-        self.var_def_map = {}  # type: Dict[str, VariableDefinition]
-
-    def enter_OperationDefinition(
-        self,
-        node,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[Union[int, str]]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> None
-        self.var_def_map = {}
-
-    def leave_OperationDefinition(
-        self,
-        operation,  # type: OperationDefinition
-        key,  # type: int
-        parent,  # type: List[OperationDefinition]
-        path,  # type: List[str]
-        ancestors,  # type: List[Document]
-    ):
-        # type: (...) -> None
-        usages = self.context.get_recursive_variable_usages(operation)
-
-        for usage in usages:
-            node = usage.node
-            type = usage.type
-            var_name = node.name.value
-            var_def = self.var_def_map.get(var_name)
-            if var_def and type:
-                # A var type is allowed if it is the same or more strict (e.g. is
-                # a subtype of) than the expected type. It can be more strict if
-                # the variable type is non-null when the expected type is nullable.
-                # If both are list types, the variable item type can be more strict
-                # than the expected item type (contravariant).
-                schema = self.context.get_schema()
-                var_type = type_from_ast(schema, var_def.type)
-                if var_type and not is_type_sub_type_of(
-                    schema, self.effective_type(var_type, var_def), type
-                ):
-                    self.context.report_error(
-                        GraphQLError(
-                            self.bad_var_pos_message(var_name, var_type, type),
-                            [var_def, node],
-                        )
-                    )
-
-    def enter_VariableDefinition(
-        self,
-        node,  # type: VariableDefinition
-        key,  # type: int
-        parent,  # type: Any
-        path,  # type: List[str]
-        ancestors,  # type: List[Document]
-    ):
-
-        self.var_def_map[node.variable.name.value] = node
-
-    @staticmethod
-    def effective_type(var_type, var_def):
-        if not var_def.default_value or isinstance(var_type, GraphQLNonNull):
-            return var_type
-
-        return GraphQLNonNull(var_type)
-
-    @staticmethod
-    def bad_var_pos_message(var_name, var_type, expected_type):
-        return 'Variable "{}" of type "{}" used in position expecting type "{}".'.format(
-            var_name, var_type, expected_type
-        )
diff --git a/graphql/validation/tests/__init__.py b/graphql/validation/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/graphql/validation/tests/test_arguments_of_correct_type.py b/graphql/validation/tests/test_arguments_of_correct_type.py
deleted file mode 100644
index db16566..0000000
--- a/graphql/validation/tests/test_arguments_of_correct_type.py
+++ /dev/null
@@ -1,876 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import ArgumentsOfCorrectType
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def bad_value(arg_name, type_name, value, line, column, errors=None):
-    if not errors:
-        errors = [u'Expected type "{}", found {}.'.format(type_name, value)]
-
-    return {
-        "message": ArgumentsOfCorrectType.bad_value_message(
-            arg_name, type_name, value, errors
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-# noinspection PyMethodMayBeStatic
-class TestValidValues(object):
-    def test_good_int_value(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                intArgField(intArg: 2)
-            }
-        }
-        """,
-        )
-
-    def test_good_boolean_value(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-              booleanArgField(booleanArg: true)
-            }
-        }
-        """,
-        )
-
-    def test_good_string_value(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringArgField(stringArg: "foo")
-            }
-        }
-        """,
-        )
-
-    def test_good_float_value(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                floatArgField(floatArg: 1.1)
-            }
-        }
-        """,
-        )
-
-    def test_int_into_float(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                floatArgField(floatArg: 1)
-            }
-        }
-        """,
-        )
-
-    def test_int_into_id(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-              idArgField(idArg: 1)
-            }
-        }
-        """,
-        )
-
-    def test_string_into_id(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-              idArgField(idArg: "someIdString")
-            }
-        }
-        """,
-        )
-
-    def test_good_enum_value(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                doesKnowCommand(dogCommand: SIT)
-            }
-        }
-        """,
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidStringValues(object):
-    def test_int_into_string(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringArgField(stringArg: 1)
-            }
-        }
-        """,
-            [bad_value("stringArg", "String", "1", 4, 43)],
-        )
-
-    def test_float_into_string(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringArgField(stringArg: 1.0)
-            }
-        }
-        """,
-            [bad_value("stringArg", "String", "1.0", 4, 43)],
-        )
-
-    def test_bool_into_string(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringArgField(stringArg: true)
-            }
-        }
-        """,
-            [bad_value("stringArg", "String", "true", 4, 43)],
-        )
-
-    def test_unquoted_string_into_string(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringArgField(stringArg: BAR)
-            }
-        }
-        """,
-            [bad_value("stringArg", "String", "BAR", 4, 43)],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidIntValues(object):
-    def test_string_into_int(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                intArgField(intArg: "3")
-            }
-        }
-        """,
-            [bad_value("intArg", "Int", '"3"', 4, 37)],
-        )
-
-    def test_big_int_into_int(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                intArgField(intArg: 829384293849283498239482938)
-            }
-        }
-        """,
-            [bad_value("intArg", "Int", "829384293849283498239482938", 4, 37)],
-        )
-
-    def test_unquoted_string_into_int(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                intArgField(intArg: FOO)
-            }
-        }
-        """,
-            [bad_value("intArg", "Int", "FOO", 4, 37)],
-        )
-
-    def test_simple_float_into_int(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                intArgField(intArg: 3.0)
-            }
-        }
-        """,
-            [bad_value("intArg", "Int", "3.0", 4, 37)],
-        )
-
-    def test_float_into_int(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                intArgField(intArg: 3.333)
-            }
-        }
-        """,
-            [bad_value("intArg", "Int", "3.333", 4, 37)],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidFloatValues(object):
-    def test_string_into_float(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                floatArgField(floatArg: "3.333")
-            }
-        }
-        """,
-            [bad_value("floatArg", "Float", '"3.333"', 4, 41)],
-        )
-
-    def test_boolean_into_float(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                floatArgField(floatArg: true)
-            }
-        }
-        """,
-            [bad_value("floatArg", "Float", "true", 4, 41)],
-        )
-
-    def test_unquoted_into_float(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                floatArgField(floatArg: FOO)
-            }
-        }
-        """,
-            [bad_value("floatArg", "Float", "FOO", 4, 41)],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidBooleanValues(object):
-    def test_int_into_boolean(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                booleanArgField(booleanArg: 2)
-            }
-        }
-        """,
-            [bad_value("booleanArg", "Boolean", "2", 4, 45)],
-        )
-
-    def test_float_into_boolean(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                booleanArgField(booleanArg: 1.0)
-            }
-        }
-        """,
-            [bad_value("booleanArg", "Boolean", "1.0", 4, 45)],
-        )
-
-    def test_string_into_boolean(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                booleanArgField(booleanArg: "true")
-            }
-        }
-        """,
-            [bad_value("booleanArg", "Boolean", '"true"', 4, 45)],
-        )
-
-    def test_unquoted_into_boolean(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                booleanArgField(booleanArg: TRUE)
-            }
-        }
-        """,
-            [bad_value("booleanArg", "Boolean", "TRUE", 4, 45)],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidIDValues(object):
-    def test_float_into_id(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                idArgField(idArg: 1.0)
-            }
-        }
-        """,
-            [bad_value("idArg", "ID", "1.0", 4, 35)],
-        )
-
-    def test_boolean_into_id(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                idArgField(idArg: true)
-            }
-        }
-        """,
-            [bad_value("idArg", "ID", "true", 4, 35)],
-        )
-
-    def test_unquoted_into_id(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                idArgField(idArg: SOMETHING)
-            }
-        }
-        """,
-            [bad_value("idArg", "ID", "SOMETHING", 4, 35)],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidEnumValues(object):
-    def test_int_into_enum(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                doesKnowCommand(dogCommand: 2)
-            }
-        }
-        """,
-            [bad_value("dogCommand", "DogCommand", "2", 4, 45)],
-        )
-
-    def test_float_into_enum(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                doesKnowCommand(dogCommand: 1.0)
-            }
-        }
-        """,
-            [bad_value("dogCommand", "DogCommand", "1.0", 4, 45)],
-        )
-
-    def test_string_into_enum(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                doesKnowCommand(dogCommand: "SIT")
-            }
-        }
-        """,
-            [bad_value("dogCommand", "DogCommand", '"SIT"', 4, 45)],
-        )
-
-    def test_boolean_into_enum(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                doesKnowCommand(dogCommand: true)
-            }
-        }
-        """,
-            [bad_value("dogCommand", "DogCommand", "true", 4, 45)],
-        )
-
-    def test_unknown_enum_value_into_enum(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                doesKnowCommand(dogCommand: JUGGLE)
-            }
-        }
-        """,
-            [bad_value("dogCommand", "DogCommand", "JUGGLE", 4, 45)],
-        )
-
-    def test_different_case_enum_value_into_enum(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                doesKnowCommand(dogCommand: sit)
-            }
-        }
-        """,
-            [bad_value("dogCommand", "DogCommand", "sit", 4, 45)],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestValidListValues(object):
-    def test_good_list_value(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringListArgField(stringListArg: ["one", "two"])
-            }
-        }
-        """,
-        )
-
-    def test_empty_list_value(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringListArgField(stringListArg: [])
-            }
-        }
-        """,
-        )
-
-    def test_single_value_into_list(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringListArgField(stringListArg: "one")
-            }
-        }
-        """,
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidListValues(object):
-    def test_incorrect_item_type(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringListArgField(stringListArg: ["one", 2])
-            }
-        }
-        """,
-            [
-                bad_value(
-                    "stringListArg",
-                    "String",
-                    '["one", 2]',
-                    4,
-                    51,
-                    ['In element #1: Expected type "String", found 2.'],
-                )
-            ],
-        )
-
-    def test_single_value_of_incorrect_type(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                stringListArgField(stringListArg: 1)
-            }
-        }
-        """,
-            [bad_value("stringListArg", "String", "1", 4, 51)],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestValidNonNullableValues(object):
-    def test_arg_on_optional_arg(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                isHousetrained(atOtherHomes: true)
-            }
-        }
-        """,
-        )
-
-    def test_no_arg_on_optional_arg(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog {
-                isHousetrained
-            }
-        }
-        """,
-        )
-
-    def test_multiple_args(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleReqs(req1: 1, req2: 2)
-            }
-        }
-        """,
-        )
-
-    def test_multiple_args_reverse_order(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleReqs(req2: 2, req1: 1)
-            }
-        }
-        """,
-        )
-
-    def test_no_args_on_multiple_optional(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleOpts
-            }
-        }
-        """,
-        )
-
-    def test_one_arg_on_multiple_optional(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleOpts(opt1: 1)
-            }
-        }
-        """,
-        )
-
-    def test_second_arg_on_multiple_optional(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleOpts(opt2: 1)
-            }
-        }
-        """,
-        )
-
-    def test_multiple_reqs_and_one_opt_on_mixed_list(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleOpts(req1: 3, req2: 4, opt1: 5)
-            }
-        }
-        """,
-        )
-
-    def test_all_reqs_and_opts_on_mixed_list(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleOpts(req1: 3, req2: 4, opt1: 5, opt2: 6)
-            }
-        }
-        """,
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidNonNullableValues(object):
-    def test_incorrect_value_type(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleOptsAndReq(req2: "two", req1: "one")
-            }
-        }
-        """,
-            [
-                bad_value("req2", "Int", '"two"', 4, 42),
-                bad_value("req1", "Int", '"one"', 4, 55),
-            ],
-        )
-
-    def test_incorrect_value_and_missing_argument(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                multipleReqs(req1: "one")
-            }
-        }
-        """,
-            [bad_value("req1", "Int", '"one"', 4, 36)],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestValidInputObjectValue(object):
-    def test_optional_arg_despite_required_field_in_type(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField
-            }
-        }
-        """,
-        )
-
-    def test_partial_object_only_required(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField(complexArg: { requiredField: true })
-            }
-        }
-        """,
-        )
-
-    def test_partial_object_required_field_can_be_falsey(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField(complexArg: { requiredField: false })
-            }
-        }
-        """,
-        )
-
-    def test_partial_object_including_required(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField(complexArg: { requiredField: false, intField: 4 })
-            }
-        }
-        """,
-        )
-
-    def test_full_object(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField(complexArg: {
-                    requiredField: true,
-                    intField: 4,
-                    stringField: "foo",
-                    booleanField: false,
-                    stringListField: ["one", "two"]
-                })
-            }
-        }
-        """,
-        )
-
-    def test_full_object_with_fields_in_different_order(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField(complexArg: {
-                    stringListField: ["one", "two"]
-                    booleanField: false,
-                    requiredField: true,
-                    stringField: "foo",
-                    intField: 4,
-                })
-            }
-        }
-        """,
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestInvalidInputObjectValue(object):
-    def test_partial_object_missing_required(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField(complexArg: { intField: 4 })
-            }
-        }
-        """,
-            [
-                bad_value(
-                    "complexArg",
-                    "ComplexInput",
-                    "{intField: 4}",
-                    4,
-                    45,
-                    ['In field "requiredField": Expected "Boolean!", found null.'],
-                )
-            ],
-        )
-
-    def test_partial_object_invalid_field_type(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField(complexArg: {
-                    stringListField: ["one", 2],
-                    requiredField: true
-                })
-            }
-        }
-        """,
-            [
-                bad_value(
-                    "complexArg",
-                    "ComplexInput",
-                    '{stringListField: ["one", 2], requiredField: true}',
-                    4,
-                    45,
-                    [
-                        'In field "stringListField": In element #1: Expected type "String", found 2.'
-                    ],
-                )
-            ],
-        )
-
-    def test_partial_object_unknown_field_arg(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            complicatedArgs {
-                complexArgField(complexArg: {
-                    requiredField: true
-                    unknownField: "value",
-                })
-            }
-        }
-        """,
-            [
-                bad_value(
-                    "complexArg",
-                    "ComplexInput",
-                    '{requiredField: true, unknownField: "value"}',
-                    4,
-                    45,
-                    ['In field "unknownField": Unknown field.'],
-                )
-            ],
-        )
-
-
-# noinspection PyMethodMayBeStatic
-class TestDirectiveArguments(object):
-    def test_with_directives_of_valid_types(self):
-        expect_passes_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog @include(if: true) {
-                name
-            }
-            human @skip(if: false) {
-                name
-            }
-        }
-        """,
-        )
-
-    def test_with_directive_with_incorrect_types(self):
-        expect_fails_rule(
-            ArgumentsOfCorrectType,
-            """
-        {
-            dog @include(if: "yes") {
-                name @skip(if: ENUM)
-            }
-        }
-        """,
-            [
-                bad_value("if", "Boolean", '"yes"', 3, 30),
-                bad_value("if", "Boolean", "ENUM", 4, 32),
-            ],
-        )
diff --git a/graphql/validation/tests/test_default_values_of_correct_type.py b/graphql/validation/tests/test_default_values_of_correct_type.py
deleted file mode 100644
index 9ea0b43..0000000
--- a/graphql/validation/tests/test_default_values_of_correct_type.py
+++ /dev/null
@@ -1,146 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import DefaultValuesOfCorrectType
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def default_for_non_null_arg(var_name, type_name, guess_type_name, line, column):
-    return {
-        "message": DefaultValuesOfCorrectType.default_for_non_null_arg_message(
-            var_name, type_name, guess_type_name
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def bad_value(var_name, type_name, value, line, column, errors=None):
-    if not errors:
-        errors = ['Expected type "{}", found {}.'.format(type_name, value)]
-
-    return {
-        "message": DefaultValuesOfCorrectType.bad_value_for_default_arg_message(
-            var_name, type_name, value, errors
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_variables_with_no_default_values():
-    return expect_passes_rule(
-        DefaultValuesOfCorrectType,
-        """
-    query NullableValues($a: Int, $b: String, $c: ComplexInput) {
-        dog { name }
-    }
-    """,
-    )
-
-
-def test_required_variables_without_default_values():
-    expect_passes_rule(
-        DefaultValuesOfCorrectType,
-        """
-    query RequiredValues($a: Int!, $b: String!) {
-        dog { name }
-    }
-    """,
-    )
-
-
-def test_variables_with_valid_default_values():
-    expect_passes_rule(
-        DefaultValuesOfCorrectType,
-        """
-    query WithDefaultValues(
-        $a: Int = 1,
-        $b: String = "ok",
-        $c: ComplexInput = { requiredField: true, intField: 3 }
-    ) {
-        dog { name }
-    }
-    """,
-    )
-
-
-def test_no_required_variables_with_default_values():
-    expect_fails_rule(
-        DefaultValuesOfCorrectType,
-        """
-    query UnreachableDefaultValues($a: Int! = 3, $b: String! = "default") {
-        dog { name }
-    }
-    """,
-        [
-            default_for_non_null_arg("a", "Int!", "Int", 2, 47),
-            default_for_non_null_arg("b", "String!", "String", 2, 64),
-        ],
-    )
-
-
-def test_variables_with_invalid_default_values():
-    expect_fails_rule(
-        DefaultValuesOfCorrectType,
-        """
-    query InvalidDefaultValues(
-        $a: Int = "one",
-        $b: String = 4,
-        $c: ComplexInput = "notverycomplex"
-    ) {
-        dog { name }
-    }
-    """,
-        [
-            bad_value("a", "Int", '"one"', 3, 19),
-            bad_value("b", "String", "4", 4, 22),
-            bad_value(
-                "c",
-                "ComplexInput",
-                '"notverycomplex"',
-                5,
-                28,
-                ['Expected "ComplexInput", found not an object.'],
-            ),
-        ],
-    )
-
-
-def test_variables_missing_required_field():
-    expect_fails_rule(
-        DefaultValuesOfCorrectType,
-        """
-    query MissingRequiredField($a: ComplexInput = {intField: 3}) {
-        dog { name }
-    }
-    """,
-        [
-            bad_value(
-                "a",
-                "ComplexInput",
-                "{intField: 3}",
-                2,
-                51,
-                ['In field "requiredField": Expected "Boolean!", found null.'],
-            )
-        ],
-    )
-
-
-def test_list_variables_with_invalid_item():
-    expect_fails_rule(
-        DefaultValuesOfCorrectType,
-        """
-    query invalidItem($a: [String] = ["one", 2]) {
-        dog { name }
-    }
-    """,
-        [
-            bad_value(
-                "a",
-                "[String]",
-                '["one", 2]',
-                2,
-                38,
-                ['In element #1: Expected type "String", found 2.'],
-            )
-        ],
-    )
diff --git a/graphql/validation/tests/test_fields_on_correct_type.py b/graphql/validation/tests/test_fields_on_correct_type.py
deleted file mode 100644
index 20c1bcb..0000000
--- a/graphql/validation/tests/test_fields_on_correct_type.py
+++ /dev/null
@@ -1,305 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules.fields_on_correct_type import (
-    FieldsOnCorrectType,
-    _undefined_field_message,
-)
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def undefined_field(field, gql_type, suggested_types, suggested_fields, line, column):
-    return {
-        "message": _undefined_field_message(
-            field, gql_type, suggested_types, suggested_fields
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_object_field_selection():
-    expect_passes_rule(
-        FieldsOnCorrectType,
-        """
-      fragment objectFieldSelection on Dog {
-        __typename
-        name
-      }
-    """,
-    )
-
-
-def test_aliased_object_field_selection():
-    expect_passes_rule(
-        FieldsOnCorrectType,
-        """
-      fragment aliasedObjectFieldSelection on Dog {
-        tn : __typename
-        otherName : name
-      }
-    """,
-    )
-
-
-def test_interface_field_selection():
-    expect_passes_rule(
-        FieldsOnCorrectType,
-        """
-      fragment interfaceFieldSelection on Pet {
-        __typename
-        name
-      }
-    """,
-    )
-
-
-def test_aliased_interface_field_selection():
-    expect_passes_rule(
-        FieldsOnCorrectType,
-        """
-      fragment interfaceFieldSelection on Pet {
-        otherName : name
-      }
-    """,
-    )
-
-
-def test_lying_alias_selection():
-    expect_passes_rule(
-        FieldsOnCorrectType,
-        """
-      fragment lyingAliasSelection on Dog {
-        name : nickname
-      }
-    """,
-    )
-
-
-def test_ignores_fields_on_unknown_type():
-    expect_passes_rule(
-        FieldsOnCorrectType,
-        """
-      fragment unknownSelection on UnknownType {
-        unknownField
-      }
-    """,
-    )
-
-
-def test_reports_errors_when_type_is_known_again():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment typeKnownAgain on Pet {
-        unknown_pet_field {
-          ... on Cat {
-            unknown_cat_field
-          }
-        }
-      },
-    """,
-        [
-            undefined_field("unknown_pet_field", "Pet", [], [], 3, 9),
-            undefined_field("unknown_cat_field", "Cat", [], [], 5, 13),
-        ],
-    )
-
-
-def test_field_not_defined_on_fragment():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment fieldNotDefined on Dog {
-        meowVolume
-      }
-    """,
-        [undefined_field("meowVolume", "Dog", [], ["barkVolume"], 3, 9)],
-    )
-
-
-def test_ignores_deeply_unknown_field():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment deepFieldNotDefined on Dog {
-        unknown_field {
-          deeper_unknown_field
-        }
-      }
-    """,
-        [undefined_field("unknown_field", "Dog", [], [], 3, 9)],
-    )
-
-
-def test_sub_field_not_defined():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment subFieldNotDefined on Human {
-        pets {
-          unknown_field
-        }
-      }
-    """,
-        [undefined_field("unknown_field", "Pet", [], [], 4, 11)],
-    )
-
-
-def test_field_not_defined_on_inline_fragment():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment fieldNotDefined on Pet {
-        ... on Dog {
-          meowVolume
-        }
-      }
-    """,
-        [undefined_field("meowVolume", "Dog", [], ["barkVolume"], 4, 11)],
-    )
-
-
-def test_aliased_field_target_not_defined():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment aliasedFieldTargetNotDefined on Dog {
-        volume : mooVolume
-      }
-    """,
-        [undefined_field("mooVolume", "Dog", [], ["barkVolume"], 3, 9)],
-    )
-
-
-def test_aliased_lying_field_target_not_defined():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment aliasedLyingFieldTargetNotDefined on Dog {
-        barkVolume : kawVolume
-      }
-    """,
-        [undefined_field("kawVolume", "Dog", [], ["barkVolume"], 3, 9)],
-    )
-
-
-def test_not_defined_on_interface():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment notDefinedOnInterface on Pet {
-        tailLength
-      }
-    """,
-        [undefined_field("tailLength", "Pet", [], [], 3, 9)],
-    )
-
-
-def test_defined_on_implementors_but_not_on_interface():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment definedOnImplementorsButNotInterface on Pet {
-        nickname
-      }
-    """,
-        [undefined_field("nickname", "Pet", ["Dog", "Cat"], ["name"], 3, 9)],
-    )
-
-
-def test_meta_field_selection_on_union():
-    expect_passes_rule(
-        FieldsOnCorrectType,
-        """
-      fragment directFieldSelectionOnUnion on CatOrDog {
-        __typename
-      }
-    """,
-    )
-
-
-def test_direct_field_selection_on_union():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment directFieldSelectionOnUnion on CatOrDog {
-        directField
-      }
-    """,
-        [undefined_field("directField", "CatOrDog", [], [], 3, 9)],
-    )
-
-
-def test_defined_on_implementors_queried_on_union():
-    expect_fails_rule(
-        FieldsOnCorrectType,
-        """
-      fragment definedOnImplementorsQueriedOnUnion on CatOrDog {
-        name
-      }
-    """,
-        [
-            undefined_field(
-                "name", "CatOrDog", ["Being", "Pet", "Canine", "Dog", "Cat"], [], 3, 9
-            )
-        ],
-    )
-
-
-def test_valid_field_in_inline_fragment():
-    expect_passes_rule(
-        FieldsOnCorrectType,
-        """
-      fragment objectFieldSelection on Pet {
-        ... on Dog {
-          name
-        }
-        ... {
-          name
-        }
-      }
-    """,
-    )
-
-
-def test_fields_correct_type_no_suggestion():
-    message = _undefined_field_message("f", "T", [], [])
-    assert message == 'Cannot query field "f" on type "T".'
-
-
-def test_works_with_no_small_numbers_of_type_suggestion():
-    message = _undefined_field_message("f", "T", ["A", "B"], [])
-    assert message == (
-        'Cannot query field "f" on type "T". '
-        + 'Did you mean to use an inline fragment on "A" or "B"?'
-    )
-
-
-def test_works_with_no_small_numbers_of_field_suggestion():
-    message = _undefined_field_message("f", "T", [], ["z", "y"])
-    assert message == (
-        'Cannot query field "f" on type "T". ' + 'Did you mean "z" or "y"?'
-    )
-
-
-def test_only_shows_one_set_of_suggestions_at_a_time_preferring_types():
-    message = _undefined_field_message("f", "T", ["A", "B"], ["z", "y"])
-    assert message == (
-        'Cannot query field "f" on type "T". '
-        + 'Did you mean to use an inline fragment on "A" or "B"?'
-    )
-
-
-def test_limits_lots_of_type_suggestions():
-    message = _undefined_field_message("f", "T", ["A", "B", "C", "D", "E", "F"], [])
-    assert message == (
-        'Cannot query field "f" on type "T". '
-        + 'Did you mean to use an inline fragment on "A", "B", "C", "D" or "E"?'
-    )
-
-
-def test_limits_lots_of_field_suggestions():
-    message = _undefined_field_message("f", "T", [], ["z", "y", "x", "w", "v", "u"])
-    assert message == (
-        'Cannot query field "f" on type "T". '
-        + 'Did you mean "z", "y", "x", "w" or "v"?'
-    )
diff --git a/graphql/validation/tests/test_fragments_on_composite_types.py b/graphql/validation/tests/test_fragments_on_composite_types.py
deleted file mode 100644
index 0c4c435..0000000
--- a/graphql/validation/tests/test_fragments_on_composite_types.py
+++ /dev/null
@@ -1,131 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import FragmentsOnCompositeTypes
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def fragment_on_non_composite_error(frag_name, type_name, line, column):
-    return {
-        "message": FragmentsOnCompositeTypes.fragment_on_non_composite_error_message(
-            frag_name, type_name
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def inline_fragment_on_non_composite_error(type_name, line, column):
-    return {
-        "message": FragmentsOnCompositeTypes.inline_fragment_on_non_composite_error_message(
-            type_name
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_object_is_valid_fragment_type():
-    expect_passes_rule(
-        FragmentsOnCompositeTypes,
-        """
-      fragment validFragment on Dog {
-        barks
-      }
-    """,
-    )
-
-
-def test_interface_is_valid_fragment_type():
-    expect_passes_rule(
-        FragmentsOnCompositeTypes,
-        """
-      fragment validFragment on Pet {
-        name
-      }
-    """,
-    )
-
-
-def test_object_is_valid_inline_fragment_type():
-    expect_passes_rule(
-        FragmentsOnCompositeTypes,
-        """
-      fragment validFragment on Pet {
-        ... on Dog {
-          barks
-        }
-      }
-    """,
-    )
-
-
-def test_inline_fragment_without_type_is_valid():
-    expect_passes_rule(
-        FragmentsOnCompositeTypes,
-        """
-    fragment validFragment on Pet {
-      ... {
-        name
-      }
-    }
-    """,
-    )
-
-
-def test_union_is_valid_fragment_type():
-    expect_passes_rule(
-        FragmentsOnCompositeTypes,
-        """
-      fragment validFragment on CatOrDog {
-        __typename
-      }
-    """,
-    )
-
-
-def test_scalar_is_invalid_fragment_type():
-    expect_fails_rule(
-        FragmentsOnCompositeTypes,
-        """
-      fragment scalarFragment on Boolean {
-        bad
-      }
-    """,
-        [fragment_on_non_composite_error("scalarFragment", "Boolean", 2, 34)],
-    )
-
-
-def test_enum_is_invalid_fragment_type():
-    expect_fails_rule(
-        FragmentsOnCompositeTypes,
-        """
-      fragment scalarFragment on FurColor {
-        bad
-      }
-    """,
-        [fragment_on_non_composite_error("scalarFragment", "FurColor", 2, 34)],
-    )
-
-
-def test_input_object_is_invalid_fragment_type():
-    expect_fails_rule(
-        FragmentsOnCompositeTypes,
-        """
-      fragment inputFragment on ComplexInput {
-        stringField
-      }
-    """,
-        [fragment_on_non_composite_error("inputFragment", "ComplexInput", 2, 33)],
-    )
-
-
-def test_scalar_is_invalid_inline_fragment_type():
-    expect_fails_rule(
-        FragmentsOnCompositeTypes,
-        """
-      fragment invalidFragment on Pet {
-        ... on String {
-          barks
-        }
-      }
-    """,
-        [inline_fragment_on_non_composite_error("String", 3, 16)],
-    )
diff --git a/graphql/validation/tests/test_known_argument_names.py b/graphql/validation/tests/test_known_argument_names.py
deleted file mode 100644
index cbe6c05..0000000
--- a/graphql/validation/tests/test_known_argument_names.py
+++ /dev/null
@@ -1,175 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules.known_argument_names import (
-    KnownArgumentNames,
-    _unknown_arg_message,
-    _unknown_directive_arg_message,
-)
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def unknown_arg(arg_name, field_name, type_name, suggested_args, line, column):
-    return {
-        "message": _unknown_arg_message(
-            arg_name, field_name, type_name, suggested_args
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def unknown_directive_arg(arg_name, directive_name, suggested_args, line, column):
-    return {
-        "message": _unknown_directive_arg_message(
-            arg_name, directive_name, suggested_args
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_single_arg_is_known():
-    expect_passes_rule(
-        KnownArgumentNames,
-        """
-        fragment argOnRequiredArg on Dog {
-          doesKnowCommand(dogCommand: SIT)
-        }
-    """,
-    )
-
-
-def test_multiple_args_are_known():
-    expect_passes_rule(
-        KnownArgumentNames,
-        """
-      fragment multipleArgs on ComplicatedArgs {
-        multipleReqs(req1: 1, req2: 2)
-      }
-    """,
-    )
-
-
-def test_ignore_args_of_unknown_fields():
-    expect_passes_rule(
-        KnownArgumentNames,
-        """
-      fragment argOnUnknownField on Dog {
-        unknownField(unknownArg: SIT)
-      }
-    """,
-    )
-
-
-def test_multiple_args_in_reverse_order_are_known():
-    expect_passes_rule(
-        KnownArgumentNames,
-        """
-      fragment multipleArgsReverseOrder on ComplicatedArgs {
-        multipleReqs(req2: 2, req1: 1)
-      }
-    """,
-    )
-
-
-def test_no_args_on_optional_arg():
-    expect_passes_rule(
-        KnownArgumentNames,
-        """
-      fragment noArgOnOptionalArg on Dog {
-        isHousetrained
-      }
-    """,
-    )
-
-
-def test_args_are_known_deeply():
-    expect_passes_rule(
-        KnownArgumentNames,
-        """
-      {
-        dog {
-          doesKnowCommand(dogCommand: SIT)
-        }
-        human {
-          pet {
-            ... on Dog {
-                doesKnowCommand(dogCommand: SIT)
-            }
-          }
-        }
-      }
-    """,
-    )
-
-
-def test_directive_args_are_known():
-    expect_passes_rule(
-        KnownArgumentNames,
-        """
-      {
-        dog @skip(if: true)
-      }
-    """,
-    )
-
-
-def test_undirective_args_are_invalid():
-    expect_fails_rule(
-        KnownArgumentNames,
-        """
-      {
-        dog @skip(unless: true)
-      }
-    """,
-        [unknown_directive_arg("unless", "skip", [], 3, 19)],
-    )
-
-
-def test_invalid_arg_name():
-    expect_fails_rule(
-        KnownArgumentNames,
-        """
-      fragment invalidArgName on Dog {
-        doesKnowCommand(unknown: true)
-      }
-    """,
-        [unknown_arg("unknown", "doesKnowCommand", "Dog", [], 3, 25)],
-    )
-
-
-def test_unknown_args_amongst_known_args():
-    expect_fails_rule(
-        KnownArgumentNames,
-        """
-      fragment oneGoodArgOneInvalidArg on Dog {
-        doesKnowCommand(whoknows: 1, dogCommand: SIT, unknown: true)
-      }
-    """,
-        [
-            unknown_arg("whoknows", "doesKnowCommand", "Dog", [], 3, 25),
-            unknown_arg("unknown", "doesKnowCommand", "Dog", [], 3, 55),
-        ],
-    )
-
-
-def test_unknown_args_deeply():
-    expect_fails_rule(
-        KnownArgumentNames,
-        """
-      {
-        dog {
-          doesKnowCommand(unknown: true)
-        }
-        human {
-          pet {
-            ... on Dog {
-              doesKnowCommand(unknown: true)
-            }
-          }
-        }
-      }
-    """,
-        [
-            unknown_arg("unknown", "doesKnowCommand", "Dog", [], 4, 27),
-            unknown_arg("unknown", "doesKnowCommand", "Dog", [], 9, 31),
-        ],
-    )
diff --git a/graphql/validation/tests/test_known_directives.py b/graphql/validation/tests/test_known_directives.py
deleted file mode 100644
index 2eb2a15..0000000
--- a/graphql/validation/tests/test_known_directives.py
+++ /dev/null
@@ -1,212 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import KnownDirectives
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def unknown_directive(directive_name, line, column):
-    return {
-        "message": KnownDirectives.unknown_directive_message(directive_name),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def misplaced_directive(directive_name, placement, line, column):
-    return {
-        "message": KnownDirectives.misplaced_directive_message(
-            directive_name, placement
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_with_no_directives():
-    expect_passes_rule(
-        KnownDirectives,
-        """
-      query Foo {
-        name
-        ...Frag
-      }
-
-      fragment Frag on Dog {
-        name
-      }
-    """,
-    )
-
-
-def test_with_known_directives():
-    expect_passes_rule(
-        KnownDirectives,
-        """
-      {
-        dog @include(if: true) {
-          name
-        }
-        human @skip(if: false) {
-          name
-        }
-      }
-    """,
-    )
-
-
-def test_with_unknown_directive():
-    expect_fails_rule(
-        KnownDirectives,
-        """
-      {
-        dog @unknown(directive: "value") {
-          name
-        }
-      }
-    """,
-        [unknown_directive("unknown", 3, 13)],
-    )
-
-
-def test_with_many_unknown_directives():
-    expect_fails_rule(
-        KnownDirectives,
-        """
-      {
-        dog @unknown(directive: "value") {
-          name
-        }
-        human @unknown(directive: "value") {
-          name
-          pets @unknown(directive: "value") {
-            name
-          }
-        }
-      }
-    """,
-        [
-            unknown_directive("unknown", 3, 13),
-            unknown_directive("unknown", 6, 15),
-            unknown_directive("unknown", 8, 16),
-        ],
-    )
-
-
-def test_with_well_placed_directives():
-    expect_passes_rule(
-        KnownDirectives,
-        """
-      query Foo @onQuery{
-        name @include(if: true)
-        ...Frag @include(if: true)
-        skippedField @skip(if: true)
-        ...SkippedFrag @skip(if: true)
-      }
-
-      mutation Bar @onMutation {
-        someField
-      }
-    """,
-    )
-
-
-def test_with_misplaced_directives():
-    expect_fails_rule(
-        KnownDirectives,
-        """
-      query Foo @include(if: true) {
-        name @onQuery
-        ...Frag @onQuery
-      }
-
-      mutation Bar @onQuery {
-        someField
-      }
-    """,
-        [
-            misplaced_directive("include", "QUERY", 2, 17),
-            misplaced_directive("onQuery", "FIELD", 3, 14),
-            misplaced_directive("onQuery", "FRAGMENT_SPREAD", 4, 17),
-            misplaced_directive("onQuery", "MUTATION", 7, 20),
-        ],
-    )
-
-
-# within schema language
-
-
-def test_within_schema_language_with_well_placed_directives():
-    expect_passes_rule(
-        KnownDirectives,
-        """
-      type MyObj implements MyInterface @onObject {
-        myField(myArg: Int @onArgumentDefinition): String @onFieldDefinition
-      }
-
-      scalar MyScalar @onScalar
-
-      interface MyInterface @onInterface {
-        myField(myArg: Int @onArgumentDefinition): String @onFieldDefinition
-      }
-
-      union MyUnion @onUnion = MyObj | Other
-
-      enum MyEnum @onEnum {
-        MY_VALUE @onEnumValue
-      }
-
-      input MyInput @onInputObject {
-        myField: Int @onInputFieldDefinition
-      }
-
-      schema @OnSchema {
-        query: MyQuery
-      }
-    """,
-    )
-
-
-def test_within_schema_language_with_misplaced_directives():
-    expect_fails_rule(
-        KnownDirectives,
-        """
-        type MyObj implements MyInterface @onInterface {
-          myField(myArg: Int @onInputFieldDefinition): String @onInputFieldDefinition
-        }
-
-        scalar MyScalar @onEnum
-
-        interface MyInterface @onObject {
-          myField(myArg: Int @onInputFieldDefinition): String @onInputFieldDefinition
-        }
-
-        union MyUnion @onEnumValue = MyObj | Other
-
-        enum MyEnum @onScalar {
-          MY_VALUE @onUnion
-        }
-
-        input MyInput @onEnum {
-          myField: Int @onArgumentDefinition
-        }
-
-        schema @onObject {
-          query: MyQuery
-        }
-    """,
-        [
-            misplaced_directive("onInterface", "OBJECT", 2, 43),
-            misplaced_directive("onInputFieldDefinition", "ARGUMENT_DEFINITION", 3, 30),
-            misplaced_directive("onInputFieldDefinition", "FIELD_DEFINITION", 3, 63),
-            misplaced_directive("onEnum", "SCALAR", 6, 25),
-            misplaced_directive("onObject", "INTERFACE", 8, 31),
-            misplaced_directive("onInputFieldDefinition", "ARGUMENT_DEFINITION", 9, 30),
-            misplaced_directive("onInputFieldDefinition", "FIELD_DEFINITION", 9, 63),
-            misplaced_directive("onEnumValue", "UNION", 12, 23),
-            misplaced_directive("onScalar", "ENUM", 14, 21),
-            misplaced_directive("onUnion", "ENUM_VALUE", 15, 20),
-            misplaced_directive("onEnum", "INPUT_OBJECT", 18, 23),
-            misplaced_directive(
-                "onArgumentDefinition", "INPUT_FIELD_DEFINITION", 19, 24
-            ),
-            misplaced_directive("onObject", "SCHEMA", 22, 16),
-        ],
-    )
diff --git a/graphql/validation/tests/test_known_fragment_names.py b/graphql/validation/tests/test_known_fragment_names.py
deleted file mode 100644
index e6940fb..0000000
--- a/graphql/validation/tests/test_known_fragment_names.py
+++ /dev/null
@@ -1,65 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import KnownFragmentNames
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def undefined_fragment(fragment_name, line, column):
-    return {
-        "message": KnownFragmentNames.unknown_fragment_message(fragment_name),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_known_fragment_names_are_valid():
-    expect_passes_rule(
-        KnownFragmentNames,
-        """
-    {
-        human(id: 4) {
-            ...HumanFields1
-            ... on Human {
-                ...HumanFields2
-            }
-            ... {
-                name
-            }
-        }
-    }
-    fragment HumanFields1 on Human {
-        name
-        ...HumanFields3
-    }
-    fragment HumanFields2 on Human {
-        name
-    }
-    fragment HumanFields3 on Human {
-        name
-    }
-    """,
-    )
-
-
-def test_unknown_fragment_names_are_invalid():
-    expect_fails_rule(
-        KnownFragmentNames,
-        """
-    {
-        human(id: 4) {
-            ...UnknownFragment1
-            ... on Human {
-                ...UnknownFragment2
-            }
-        }
-    }
-    fragment HumanFields on Human {
-        name
-        ...UnknownFragment3
-    }
-    """,
-        [
-            undefined_fragment("UnknownFragment1", 4, 16),
-            undefined_fragment("UnknownFragment2", 6, 20),
-            undefined_fragment("UnknownFragment3", 12, 12),
-        ],
-    )
diff --git a/graphql/validation/tests/test_known_type_names.py b/graphql/validation/tests/test_known_type_names.py
deleted file mode 100644
index a035386..0000000
--- a/graphql/validation/tests/test_known_type_names.py
+++ /dev/null
@@ -1,76 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules.known_type_names import (
-    KnownTypeNames,
-    _unknown_type_message,
-)
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def unknown_type(type_name, suggested_types, line, column):
-    return {
-        "message": _unknown_type_message(type_name, suggested_types),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_known_type_names_are_valid():
-    expect_passes_rule(
-        KnownTypeNames,
-        """
-      query Foo($var: String, $required: [String!]!) {
-        user(id: 4) {
-          pets { ... on Pet { name }, ...PetFields, ... { name } }
-        }
-      }
-      fragment PetFields on Pet {
-        name
-      }
-    """,
-    )
-
-
-def test_unknown_type_names_are_invalid():
-    expect_fails_rule(
-        KnownTypeNames,
-        """
-      query Foo($var: JumbledUpLetters) {
-        user(id: 4) {
-          name
-          pets { ... on Badger { name }, ...PetFields, ... { name } }
-        }
-      }
-      fragment PetFields on Peettt {
-        name
-      }
-    """,
-        [
-            unknown_type("JumbledUpLetters", [], 2, 23),
-            unknown_type("Badger", [], 5, 25),
-            unknown_type("Peettt", ["Pet"], 8, 29),
-        ],
-    )
-
-
-def test_ignores_type_definitions():
-    expect_fails_rule(
-        KnownTypeNames,
-        """
-      type NotInTheSchema {
-        field: FooBar
-      }
-      interface FooBar {
-        field: NotInTheSchema
-      }
-      union U = A | B
-      input Blob {
-        field: UnknownType
-      }
-      query Foo($var: NotInTheSchema) {
-        user(id: $var) {
-          id
-        }
-      }
-    """,
-        [unknown_type("NotInTheSchema", [], 12, 23)],
-    )
diff --git a/graphql/validation/tests/test_lone_anonymous_operation.py b/graphql/validation/tests/test_lone_anonymous_operation.py
deleted file mode 100644
index 0d1e493..0000000
--- a/graphql/validation/tests/test_lone_anonymous_operation.py
+++ /dev/null
@@ -1,107 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import LoneAnonymousOperation
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def anon_not_alone(line, column):
-    return {
-        "message": LoneAnonymousOperation.anonymous_operation_not_alone_message(),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_no_operations():
-    expect_passes_rule(
-        LoneAnonymousOperation,
-        """
-      fragment fragA on Type {
-        field
-      }
-    """,
-    )
-
-
-def test_one_anon_operation():
-    expect_passes_rule(
-        LoneAnonymousOperation,
-        """
-      {
-        field
-      }
-    """,
-    )
-
-
-def test_multiple_named_operation():
-    expect_passes_rule(
-        LoneAnonymousOperation,
-        """
-      query Foo {
-        field
-      }
-
-      query Bar {
-        field
-      }
-    """,
-    )
-
-
-def test_anon_operation_with_fragment():
-    expect_passes_rule(
-        LoneAnonymousOperation,
-        """
-      {
-        ...Foo
-      }
-      fragment Foo on Type {
-        field
-      }
-    """,
-    )
-
-
-def test_multiple_anon_operations():
-    expect_fails_rule(
-        LoneAnonymousOperation,
-        """
-      {
-        fieldA
-      }
-      {
-        fieldB
-      }
-    """,
-        [anon_not_alone(2, 7), anon_not_alone(5, 7)],
-    )
-
-
-def test_anon_operation_with_a_mutation():
-    expect_fails_rule(
-        LoneAnonymousOperation,
-        """
-      {
-        fieldA
-      }
-      mutation Foo {
-        fieldB
-      }
-    """,
-        [anon_not_alone(2, 7)],
-    )
-
-
-def test_anon_operation_with_a_subscription():
-    expect_fails_rule(
-        LoneAnonymousOperation,
-        """
-      {
-        fieldA
-      }
-      subscription Foo {
-        fieldB
-      }
-    """,
-        [anon_not_alone(2, 7)],
-    )
diff --git a/graphql/validation/tests/test_no_fragment_cycles.py b/graphql/validation/tests/test_no_fragment_cycles.py
deleted file mode 100644
index 93c3331..0000000
--- a/graphql/validation/tests/test_no_fragment_cycles.py
+++ /dev/null
@@ -1,228 +0,0 @@
-from graphql.language.location import SourceLocation as L
-from graphql.validation.rules import NoFragmentCycles
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def cycle_error_message(fragment_name, spread_names, *locations):
-    return {
-        "message": NoFragmentCycles.cycle_error_message(fragment_name, spread_names),
-        "locations": list(locations),
-    }
-
-
-def test_single_reference_is_valid():
-    expect_passes_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Dog { ...fragB }
-    fragment fragB on Dog { name }
-    """,
-    )
-
-
-def test_spreading_twice_is_not_circular():
-    expect_passes_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Dog { ...fragB, ...fragB }
-    fragment fragB on Dog { name }
-    """,
-    )
-
-
-def test_spreading_twice_indirectly_is_not_circular():
-    expect_passes_rule(
-        NoFragmentCycles,
-        """
-      fragment fragA on Dog { ...fragB, ...fragC }
-      fragment fragB on Dog { ...fragC }
-      fragment fragC on Dog { name }
-    """,
-    )
-
-
-def test_double_spread_within_abstract_types():
-    expect_passes_rule(
-        NoFragmentCycles,
-        """
-      fragment nameFragment on Pet {
-        ... on Dog { name }
-        ... on Cat { name }
-      }
-      fragment spreadsInAnon on Pet {
-        ... on Dog { ...nameFragment }
-        ... on Cat { ...nameFragment }
-      }
-    """,
-    )
-
-
-def test_does_not_raise_false_positive_on_unknown_fragment():
-    expect_passes_rule(
-        NoFragmentCycles,
-        """
-      fragment nameFragment on Pet {
-        ...UnknownFragment
-      }
-    """,
-    )
-
-
-def test_spreading_recursively_within_field_fails():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Human { relatives { ...fragA } },
-    """,
-        [cycle_error_message("fragA", [], L(2, 43))],
-    )
-
-
-def test_no_spreading_itself_directly():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Dog { ...fragA }
-    """,
-        [cycle_error_message("fragA", [], L(2, 29))],
-    )
-
-
-def test_no_spreading_itself_directly_within_inline_fragment():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Pet {
-        ... on Dog {
-            ...fragA
-        }
-    }
-    """,
-        [cycle_error_message("fragA", [], L(4, 13))],
-    )
-
-
-def test_no_spreading_itself_indirectly():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Dog { ...fragB }
-    fragment fragB on Dog { ...fragA }
-    """,
-        [cycle_error_message("fragA", ["fragB"], L(2, 29), L(3, 29))],
-    )
-
-
-def test_no_spreading_itself_indirectly_reports_opposite_order():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragB on Dog { ...fragA }
-    fragment fragA on Dog { ...fragB }
-    """,
-        [cycle_error_message("fragB", ["fragA"], L(2, 29), L(3, 29))],
-    )
-
-
-def test_no_spreading_itself_indirectly_within_inline_fragment():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Pet {
-        ... on Dog {
-            ...fragB
-        }
-    }
-    fragment fragB on Pet {
-        ... on Dog {
-            ...fragA
-        }
-    }
-    """,
-        [cycle_error_message("fragA", ["fragB"], L(4, 13), L(9, 13))],
-    )
-
-
-def test_no_spreading_itself_deeply():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Dog { ...fragB }
-    fragment fragB on Dog { ...fragC }
-    fragment fragC on Dog { ...fragO }
-    fragment fragX on Dog { ...fragY }
-    fragment fragY on Dog { ...fragZ }
-    fragment fragZ on Dog { ...fragO }
-    fragment fragO on Dog { ...fragP }
-    fragment fragP on Dog { ...fragA, ...fragX }
-    """,
-        [
-            cycle_error_message(
-                "fragA",
-                ["fragB", "fragC", "fragO", "fragP"],
-                L(2, 29),
-                L(3, 29),
-                L(4, 29),
-                L(8, 29),
-                L(9, 29),
-            ),
-            cycle_error_message(
-                "fragO",
-                ["fragP", "fragX", "fragY", "fragZ"],
-                L(8, 29),
-                L(9, 39),
-                L(5, 29),
-                L(6, 29),
-                L(7, 29),
-            ),
-        ],
-    )
-
-
-def test_no_spreading_itself_deeply_two_paths():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Dog { ...fragB, ...fragC }
-    fragment fragB on Dog { ...fragA }
-    fragment fragC on Dog { ...fragA }
-    """,
-        [
-            cycle_error_message("fragA", ["fragB"], L(2, 29), L(3, 29)),
-            cycle_error_message("fragA", ["fragC"], L(2, 39), L(4, 29)),
-        ],
-    )
-
-
-def test_no_spreading_itself_deeply_two_paths_alt_reverse_order():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Dog { ...fragC }
-    fragment fragB on Dog { ...fragC }
-    fragment fragC on Dog { ...fragA, ...fragB }
-    """,
-        [
-            cycle_error_message("fragA", ["fragC"], L(2, 29), L(4, 29)),
-            cycle_error_message("fragC", ["fragB"], L(4, 39), L(3, 29)),
-        ],
-    )
-
-
-def test_no_spreading_itself_deeply_and_immediately():
-    expect_fails_rule(
-        NoFragmentCycles,
-        """
-    fragment fragA on Dog { ...fragB }
-    fragment fragB on Dog { ...fragB, ...fragC }
-    fragment fragC on Dog { ...fragA, ...fragB }
-    """,
-        [
-            cycle_error_message("fragB", [], L(3, 29)),
-            cycle_error_message(
-                "fragA", ["fragB", "fragC"], L(2, 29), L(3, 39), L(4, 29)
-            ),
-            cycle_error_message("fragB", ["fragC"], L(3, 39), L(4, 39)),
-        ],
-    )
diff --git a/graphql/validation/tests/test_no_undefined_variables.py b/graphql/validation/tests/test_no_undefined_variables.py
deleted file mode 100644
index 12116a3..0000000
--- a/graphql/validation/tests/test_no_undefined_variables.py
+++ /dev/null
@@ -1,336 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import NoUndefinedVariables
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def undefined_var(var_name, l1, c1, op_name, l2, c2):
-    return {
-        "message": NoUndefinedVariables.undefined_var_message(var_name, op_name),
-        "locations": [SourceLocation(l1, c1), SourceLocation(l2, c2)],
-    }
-
-
-def test_all_varriables_defined():
-    expect_passes_rule(
-        NoUndefinedVariables,
-        """
-        query Foo($a: String, $b: String, $c: String) {
-            field(a: $a, b: $b, c: $c)
-        }
-    """,
-    )
-
-
-def test_all_variables_deeply_defined():
-    expect_passes_rule(
-        NoUndefinedVariables,
-        """
-        query Foo($a: String, $b: String, $c: String) {
-            field(a: $a) {
-                field(b: $b) {
-                    field(c: $c)
-                }
-            }
-        }
-    """,
-    )
-
-
-def test_all_variables_deeply_in_inline_fragments_defined():
-    expect_passes_rule(
-        NoUndefinedVariables,
-        """
-        query Foo($a: String, $b: String, $c: String) {
-            ... on Type {
-                field(a: $a) {
-                    field(b: $b) {
-                        ... on Type {
-                            field(c: $c)
-                        }
-                    }
-                }
-            }
-        }
-    """,
-    )
-
-
-def test_all_variables_in_fragments_deeply_defined():
-    expect_passes_rule(
-        NoUndefinedVariables,
-        """
-        query Foo($a: String, $b: String, $c: String) {
-            ...FragA
-        }
-        fragment FragA on Type {
-            field(a: $a) {
-                ...FragB
-            }
-        }
-        fragment FragB on Type {
-            field(b: $b) {
-                ...FragC
-            }
-        }
-        fragment FragC on Type {
-            field(c: $c)
-        }
-    """,
-    )
-
-
-def test_variable_within_single_fragment_defined_in_multiple_operations():
-    expect_passes_rule(
-        NoUndefinedVariables,
-        """
-        query Foo($a: String) {
-            ...FragA
-        }
-        query Bar($a: String) {
-            ...FragA
-        }
-        fragment FragA on Type {
-            field(a: $a)
-        }
-    """,
-    )
-
-
-def test_variable_within_fragments_defined_in_operations():
-    expect_passes_rule(
-        NoUndefinedVariables,
-        """
-        query Foo($a: String) {
-            ...FragA
-        }
-        query Bar($b: String) {
-            ...FragB
-        }
-        fragment FragA on Type {
-            field(a: $a)
-        }
-        fragment FragB on Type {
-            field(b: $b)
-        }
-    """,
-    )
-
-
-def test_variable_within_recursive_fragment_defined():
-    expect_passes_rule(
-        NoUndefinedVariables,
-        """
-        query Foo($a: String) {
-            ...FragA
-        }
-        fragment FragA on Type {
-            field(a: $a) {
-                ...FragA
-            }
-        }
-    """,
-    )
-
-
-def test_variable_not_defined():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      query Foo($a: String, $b: String, $c: String) {
-        field(a: $a, b: $b, c: $c, d: $d)
-      }
-    """,
-        [undefined_var("d", 3, 39, "Foo", 2, 7)],
-    )
-
-
-def variable_not_defined_by_unnamed_query():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      {
-        field(a: $a)
-      }
-    """,
-        [undefined_var("a", 3, 18, "", 2, 7)],
-    )
-
-
-def test_multiple_variables_not_defined():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      query Foo($b: String) {
-        field(a: $a, b: $b, c: $c)
-      }
-    """,
-        [
-            undefined_var("a", 3, 18, "Foo", 2, 7),
-            undefined_var("c", 3, 32, "Foo", 2, 7),
-        ],
-    )
-
-
-def test_variable_in_fragment_not_defined_by_unnamed_query():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      {
-        ...FragA
-      }
-      fragment FragA on Type {
-        field(a: $a)
-      }
-    """,
-        [undefined_var("a", 6, 18, "", 2, 7)],
-    )
-
-
-def test_variable_in_fragment_not_defined_by_operation():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      query Foo($a: String, $b: String) {
-        ...FragA
-      }
-      fragment FragA on Type {
-        field(a: $a) {
-          ...FragB
-        }
-      }
-      fragment FragB on Type {
-        field(b: $b) {
-          ...FragC
-        }
-      }
-      fragment FragC on Type {
-        field(c: $c)
-      }
-    """,
-        [undefined_var("c", 16, 18, "Foo", 2, 7)],
-    )
-
-
-def test_multiple_variables_in_fragments_not_defined():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      query Foo($b: String) {
-        ...FragA
-      }
-      fragment FragA on Type {
-        field(a: $a) {
-          ...FragB
-        }
-      }
-      fragment FragB on Type {
-        field(b: $b) {
-          ...FragC
-        }
-      }
-      fragment FragC on Type {
-        field(c: $c)
-      }
-    """,
-        [
-            undefined_var("a", 6, 18, "Foo", 2, 7),
-            undefined_var("c", 16, 18, "Foo", 2, 7),
-        ],
-    )
-
-
-def test_single_variable_in_fragment_not_defined_by_multiple_operations():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      query Foo($a: String) {
-        ...FragAB
-      }
-      query Bar($a: String) {
-        ...FragAB
-      }
-      fragment FragAB on Type {
-        field(a: $a, b: $b)
-      }
-    """,
-        [
-            undefined_var("b", 9, 25, "Foo", 2, 7),
-            undefined_var("b", 9, 25, "Bar", 5, 7),
-        ],
-    )
-
-
-def test_variables_in_fragment_not_defined_by_multiple_operations():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      query Foo($b: String) {
-        ...FragAB
-      }
-      query Bar($a: String) {
-        ...FragAB
-      }
-      fragment FragAB on Type {
-        field(a: $a, b: $b)
-      }
-    """,
-        [
-            undefined_var("a", 9, 18, "Foo", 2, 7),
-            undefined_var("b", 9, 25, "Bar", 5, 7),
-        ],
-    )
-
-
-def test_variable_in_fragment_used_by_other_operation():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      query Foo($b: String) {
-        ...FragA
-      }
-      query Bar($a: String) {
-        ...FragB
-      }
-      fragment FragA on Type {
-        field(a: $a)
-      }
-      fragment FragB on Type {
-        field(b: $b)
-      }
-    """,
-        [
-            undefined_var("a", 9, 18, "Foo", 2, 7),
-            undefined_var("b", 12, 18, "Bar", 5, 7),
-        ],
-    )
-
-
-def test_multiple_undefined_variables_produce_multiple_errors():
-    expect_fails_rule(
-        NoUndefinedVariables,
-        """
-      query Foo($b: String) {
-        ...FragAB
-      }
-      query Bar($a: String) {
-        ...FragAB
-      }
-      fragment FragAB on Type {
-        field1(a: $a, b: $b)
-        ...FragC
-        field3(a: $a, b: $b)
-      }
-      fragment FragC on Type {
-        field2(c: $c)
-      }
-    """,
-        [
-            undefined_var("a", 9, 19, "Foo", 2, 7),
-            undefined_var("a", 11, 19, "Foo", 2, 7),
-            undefined_var("c", 14, 19, "Foo", 2, 7),
-            undefined_var("b", 9, 26, "Bar", 5, 7),
-            undefined_var("b", 11, 26, "Bar", 5, 7),
-            undefined_var("c", 14, 19, "Bar", 5, 7),
-        ],
-    )
diff --git a/graphql/validation/tests/test_no_unused_fragments.py b/graphql/validation/tests/test_no_unused_fragments.py
deleted file mode 100644
index 9cbb058..0000000
--- a/graphql/validation/tests/test_no_unused_fragments.py
+++ /dev/null
@@ -1,154 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import NoUnusedFragments
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def unused_fragment(fragment_name, line, column):
-    return {
-        "message": NoUnusedFragments.unused_fragment_message(fragment_name),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_all_fragment_names_are_used():
-    expect_passes_rule(
-        NoUnusedFragments,
-        """
-      {
-        human(id: 4) {
-          ...HumanFields1
-          ... on Human {
-            ...HumanFields2
-          }
-        }
-      }
-      fragment HumanFields1 on Human {
-        name
-        ...HumanFields3
-      }
-      fragment HumanFields2 on Human {
-        name
-      }
-      fragment HumanFields3 on Human {
-        name
-      }
-    """,
-    )
-
-
-def test_all_fragment_names_are_used_by_multiple_operations():
-    expect_passes_rule(
-        NoUnusedFragments,
-        """
-      query Foo {
-        human(id: 4) {
-          ...HumanFields1
-        }
-      }
-      query Bar {
-        human(id: 4) {
-          ...HumanFields2
-        }
-      }
-      fragment HumanFields1 on Human {
-        name
-        ...HumanFields3
-      }
-      fragment HumanFields2 on Human {
-        name
-      }
-      fragment HumanFields3 on Human {
-        name
-      }
-   """,
-    )
-
-
-def test_contains_unknown_fragments():
-    expect_fails_rule(
-        NoUnusedFragments,
-        """
-      query Foo {
-        human(id: 4) {
-          ...HumanFields1
-        }
-      }
-      query Bar {
-        human(id: 4) {
-          ...HumanFields2
-        }
-      }
-      fragment HumanFields1 on Human {
-        name
-        ...HumanFields3
-      }
-      fragment HumanFields2 on Human {
-        name
-      }
-      fragment HumanFields3 on Human {
-        name
-      }
-      fragment Unused1 on Human {
-        name
-      }
-      fragment Unused2 on Human {
-        name
-      }
-    """,
-        [unused_fragment("Unused1", 22, 7), unused_fragment("Unused2", 25, 7)],
-    )
-
-
-def test_contains_unknown_fragments_with_ref_cycle():
-    expect_fails_rule(
-        NoUnusedFragments,
-        """
-      query Foo {
-        human(id: 4) {
-          ...HumanFields1
-        }
-      }
-      query Bar {
-        human(id: 4) {
-          ...HumanFields2
-        }
-      }
-      fragment HumanFields1 on Human {
-        name
-        ...HumanFields3
-      }
-      fragment HumanFields2 on Human {
-        name
-      }
-      fragment HumanFields3 on Human {
-        name
-      }
-      fragment Unused1 on Human {
-        name
-        ...Unused2
-      }
-      fragment Unused2 on Human {
-        name
-        ...Unused1
-      }
-    """,
-        [unused_fragment("Unused1", 22, 7), unused_fragment("Unused2", 26, 7)],
-    )
-
-
-def test_contains_unknown_and_undefined_fragments():
-    expect_fails_rule(
-        NoUnusedFragments,
-        """
-      query Foo {
-        human(id: 4) {
-          ...bar
-        }
-      }
-      fragment foo on Human {
-        name
-      }
-    """,
-        [unused_fragment("foo", 7, 7)],
-    )
diff --git a/graphql/validation/tests/test_no_unused_variables.py b/graphql/validation/tests/test_no_unused_variables.py
deleted file mode 100644
index b877c3d..0000000
--- a/graphql/validation/tests/test_no_unused_variables.py
+++ /dev/null
@@ -1,229 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import NoUnusedVariables
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def unused_variable(variable_name, op_name, line, column):
-    return {
-        "message": NoUnusedVariables.unused_variable_message(variable_name, op_name),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_uses_all_variables():
-    expect_passes_rule(
-        NoUnusedVariables,
-        """
-      query ($a: String, $b: String, $c: String) {
-        field(a: $a, b: $b, c: $c)
-      }
-    """,
-    )
-
-
-def test_uses_all_variables_deeply():
-    expect_passes_rule(
-        NoUnusedVariables,
-        """
-      query Foo($a: String, $b: String, $c: String) {
-        field(a: $a) {
-          field(b: $b) {
-            field(c: $c)
-          }
-        }
-      }
-    """,
-    )
-
-
-def test_uses_all_variables_deeply_in_inline_fragments():
-    expect_passes_rule(
-        NoUnusedVariables,
-        """
-      query Foo($a: String, $b: String, $c: String) {
-        ... on Type {
-          field(a: $a) {
-            field(b: $b) {
-              ... on Type {
-                field(c: $c)
-              }
-            }
-          }
-        }
-      }
-    """,
-    )
-
-
-def test_uses_all_variables_in_fragment():
-    expect_passes_rule(
-        NoUnusedVariables,
-        """
-      query Foo($a: String, $b: String, $c: String) {
-        ...FragA
-      }
-      fragment FragA on Type {
-        field(a: $a) {
-          ...FragB
-        }
-      }
-      fragment FragB on Type {
-        field(b: $b) {
-          ...FragC
-        }
-      }
-      fragment FragC on Type {
-        field(c: $c)
-      }
-    """,
-    )
-
-
-def test_variable_used_by_fragment_in_multiple_operations():
-    expect_passes_rule(
-        NoUnusedVariables,
-        """
-      query Foo($a: String) {
-        ...FragA
-      }
-      query Bar($b: String) {
-        ...FragB
-      }
-      fragment FragA on Type {
-        field(a: $a)
-      }
-      fragment FragB on Type {
-        field(b: $b)
-      }
-    """,
-    )
-
-
-def test_variable_used_by_recursive_fragment():
-    expect_passes_rule(
-        NoUnusedVariables,
-        """
-      query Foo($a: String) {
-        ...FragA
-      }
-      fragment FragA on Type {
-        field(a: $a) {
-          ...FragA
-        }
-      }
-   """,
-    )
-
-
-def test_variable_not_used():
-    expect_fails_rule(
-        NoUnusedVariables,
-        """
-      query ($a: String, $b: String, $c: String) {
-        field(a: $a, b: $b)
-      }
-    """,
-        [unused_variable("c", None, 2, 38)],
-    )
-
-
-def test_multiple_variables_not_used():
-    expect_fails_rule(
-        NoUnusedVariables,
-        """
-      query Foo($a: String, $b: String, $c: String) {
-        field(b: $b)
-      }
-    """,
-        [unused_variable("a", "Foo", 2, 17), unused_variable("c", "Foo", 2, 41)],
-    )
-
-
-def test_variable_not_used_in_fragments():
-    expect_fails_rule(
-        NoUnusedVariables,
-        """
-      query Foo($a: String, $b: String, $c: String) {
-        ...FragA
-      }
-      fragment FragA on Type {
-        field(a: $a) {
-          ...FragB
-        }
-      }
-      fragment FragB on Type {
-        field(b: $b) {
-          ...FragC
-        }
-      }
-      fragment FragC on Type {
-        field
-      }
-    """,
-        [unused_variable("c", "Foo", 2, 41)],
-    )
-
-
-def test_multiple_variables_not_used_in_fragments():
-    expect_fails_rule(
-        NoUnusedVariables,
-        """
-      query Foo($a: String, $b: String, $c: String) {
-        ...FragA
-      }
-      fragment FragA on Type {
-        field {
-          ...FragB
-        }
-      }
-      fragment FragB on Type {
-        field(b: $b) {
-          ...FragC
-        }
-      }
-      fragment FragC on Type {
-        field
-      }
-    """,
-        [unused_variable("a", "Foo", 2, 17), unused_variable("c", "Foo", 2, 41)],
-    )
-
-
-def test_variable_not_used_by_unreferenced_fragment():
-    expect_fails_rule(
-        NoUnusedVariables,
-        """
-      query Foo($b: String) {
-        ...FragA
-      }
-      fragment FragA on Type {
-        field(a: $a)
-      }
-      fragment FragB on Type {
-        field(b: $b)
-      }
-    """,
-        [unused_variable("b", "Foo", 2, 17)],
-    )
-
-
-def test_variable_not_used_by_fragment_used_by_other_operation():
-    expect_fails_rule(
-        NoUnusedVariables,
-        """
-      query Foo($b: String) {
-        ...FragA
-      }
-      query Bar($a: String) {
-        ...FragB
-      }
-      fragment FragA on Type {
-        field(a: $a)
-      }
-      fragment FragB on Type {
-        field(b: $b)
-      }
-    """,
-        [unused_variable("b", "Foo", 2, 17), unused_variable("a", "Bar", 5, 17)],
-    )
diff --git a/graphql/validation/tests/test_overlapping_fields_can_be_merged.py b/graphql/validation/tests/test_overlapping_fields_can_be_merged.py
deleted file mode 100644
index 55a2b8c..0000000
--- a/graphql/validation/tests/test_overlapping_fields_can_be_merged.py
+++ /dev/null
@@ -1,1051 +0,0 @@
-from graphql.language.location import SourceLocation as L
-from graphql.type.definition import (
-    GraphQLArgument,
-    GraphQLField,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-)
-from graphql.type.scalars import GraphQLID, GraphQLInt, GraphQLString
-from graphql.type.schema import GraphQLSchema
-from graphql.validation.rules import OverlappingFieldsCanBeMerged
-
-from .utils import (
-    expect_fails_rule,
-    expect_fails_rule_with_schema,
-    expect_passes_rule,
-    expect_passes_rule_with_schema,
-)
-
-
-def fields_conflict(reason_name, reason, *locations):
-    return {
-        "message": OverlappingFieldsCanBeMerged.fields_conflict_message(
-            reason_name, reason
-        ),
-        "locations": list(locations),
-    }
-
-
-def test_unique_fields():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment uniqueFields on Dog {
-        name
-        nickname
-    }
-    """,
-    )
-
-
-def test_identical_fields():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment mergeIdenticalFields on Dog {
-        name
-        name
-    }
-    """,
-    )
-
-
-def test_identical_fields_with_identical_args():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
-        doesKnowCommand(dogCommand: SIT)
-        doesKnowCommand(dogCommand: SIT)
-    }
-    """,
-    )
-
-
-def test_identical_fields_with_identical_directives():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment mergeSameFieldsWithSameDirectives on Dog {
-        name @include(if: true)
-        name @include(if: true)
-    }
-    """,
-    )
-
-
-def test_different_args_with_different_aliases():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment differentArgsWithDifferentAliases on Dog {
-        knowsSit: doesKnowCommand(dogCommand: SIT)
-        knowsDown: doesKnowCommand(dogCommand: DOWN)
-    }
-    """,
-    )
-
-
-def test_different_directives_with_different_aliases():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment differentDirectivesWithDifferentAliases on Dog {
-        nameIfTrue: name @include(if: true)
-        nameIfFalse: name @include(if: false)
-    }
-    """,
-    )
-
-
-def test_different_skip_or_include_directives_accepted():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment differentDirectivesWithDifferentAliases on Dog {
-        name @include(if: true)
-        name @include(if: false)
-    }
-    """,
-    )
-
-
-def test_same_aliases_with_different_field_targets():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment sameAliasesWithDifferentFieldTargets on Dog {
-        fido: name
-        fido: nickname
-    }
-    """,
-        [
-            fields_conflict(
-                "fido", "name and nickname are different fields", L(3, 9), L(4, 9)
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_same_aliases_allowed_on_nonoverlapping_fields():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment sameAliasesWithDifferentFieldTargets on Pet {
-        ... on Dog {
-            name
-        }
-        ... on Cat {
-            name: nickname
-        }
-    }
-    """,
-    )
-
-
-def test_alias_masking_direct_field_access():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment aliasMaskingDirectFieldAccess on Dog {
-        name: nickname
-        name
-    }
-    """,
-        [
-            fields_conflict(
-                "name", "nickname and name are different fields", L(3, 9), L(4, 9)
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_diferent_args_second_adds_an_argument():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment conflictingArgs on Dog {
-        doesKnowCommand
-        doesKnowCommand(dogCommand: HEEL)
-    }
-    """,
-        [
-            fields_conflict(
-                "doesKnowCommand", "they have differing arguments", L(3, 9), L(4, 9)
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_diferent_args_second_missing_an_argument():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment conflictingArgs on Dog {
-        doesKnowCommand(dogCommand: SIT)
-        doesKnowCommand
-    }
-    """,
-        [
-            fields_conflict(
-                "doesKnowCommand", "they have differing arguments", L(3, 9), L(4, 9)
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_conflicting_args():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment conflictingArgs on Dog {
-        doesKnowCommand(dogCommand: SIT)
-        doesKnowCommand(dogCommand: HEEL)
-    }
-    """,
-        [
-            fields_conflict(
-                "doesKnowCommand", "they have differing arguments", L(3, 9), L(4, 9)
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_allows_different_args_where_no_conflict_is_possible():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    fragment conflictingArgs on Pet {
-        ... on Dog {
-            name(surname: true)
-        }
-        ... on Cat {
-            name
-        }
-    }
-    """,
-    )
-
-
-def test_encounters_conflict_in_fragments():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        ...A
-        ...B
-    }
-    fragment A on Type {
-        x: a
-    }
-    fragment B on Type {
-        x: b
-    }
-    """,
-        [fields_conflict("x", "a and b are different fields", L(7, 9), L(10, 9))],
-        sort_list=False,
-    )
-
-
-def test_reports_each_conflict_once():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-      {
-        f1 {
-          ...A
-          ...B
-        }
-        f2 {
-          ...B
-          ...A
-        }
-        f3 {
-          ...A
-          ...B
-          x: c
-        }
-      }
-      fragment A on Type {
-        x: a
-      }
-      fragment B on Type {
-        x: b
-      }
-    """,
-        [
-            fields_conflict("x", "a and b are different fields", L(18, 9), L(21, 9)),
-            fields_conflict("x", "c and a are different fields", L(14, 11), L(18, 9)),
-            fields_conflict("x", "c and b are different fields", L(14, 11), L(21, 9)),
-        ],
-        sort_list=False,
-    )
-
-
-def test_deep_conflict():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        field {
-            x: a
-        }
-        field {
-            x: b
-        }
-    }
-    """,
-        [
-            fields_conflict(
-                "field",
-                [("x", "a and b are different fields")],
-                L(3, 9),
-                L(4, 13),
-                L(6, 9),
-                L(7, 13),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_deep_conflict_with_multiple_issues():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-      {
-        field {
-          x: a
-          y: c
-        }
-        field {
-          x: b
-          y: d
-        }
-      }
-    """,
-        [
-            fields_conflict(
-                "field",
-                [
-                    ("x", "a and b are different fields"),
-                    ("y", "c and d are different fields"),
-                ],
-                L(3, 9),
-                L(4, 11),
-                L(5, 11),
-                L(7, 9),
-                L(8, 11),
-                L(9, 11),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_very_deep_conflict():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        field {
-            deepField {
-                x: a
-            }
-        },
-        field {
-            deepField {
-                x: b
-            }
-        }
-    }
-    """,
-        [
-            fields_conflict(
-                "field",
-                [["deepField", [["x", "a and b are different fields"]]]],
-                L(3, 9),
-                L(4, 13),
-                L(5, 17),
-                L(8, 9),
-                L(9, 13),
-                L(10, 17),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_reports_deep_conflict_to_nearest_common_ancestor():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        field {
-            deepField {
-                x: a
-            }
-            deepField {
-                x: b
-            }
-        },
-        field {
-            deepField {
-                y
-            }
-        }
-    }
-    """,
-        [
-            fields_conflict(
-                "deepField",
-                [("x", "a and b are different fields")],
-                L(4, 13),
-                L(5, 17),
-                L(7, 13),
-                L(8, 17),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_reports_deep_conflict_to_nearest_common_ancestor_in_fragments():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-      {
-        field {
-          ...F
-        },
-        field {
-          ...F
-        }
-      }
-      fragment F on T {
-        deepField {
-          deeperField {
-            x: a
-          }
-          deeperField {
-            x: b
-          }
-        }
-        deepField {
-          deeperField {
-            y
-          }
-        }
-      }
-    """,
-        [
-            fields_conflict(
-                "deeperField",
-                [("x", "a and b are different fields")],
-                L(12, 11),
-                L(13, 13),
-                L(15, 11),
-                L(16, 13),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_reports_deep_conflict_in_nested_fragments():
-    expect_fails_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-      {
-        field {
-          ...F
-        },
-        field {
-          ...I
-        }
-      }
-      fragment F on T {
-        x: a
-        ...G
-      }
-      fragment G on T {
-        y: c
-      }
-      fragment I on T {
-        y: d
-        ...J
-      }
-      fragment J on T {
-        x: b
-      }
-    """,
-        [
-            fields_conflict(
-                "field",
-                [
-                    ("x", "a and b are different fields"),
-                    ("y", "c and d are different fields"),
-                ],
-                L(3, 9),
-                L(11, 9),
-                L(15, 9),
-                L(6, 9),
-                L(22, 9),
-                L(18, 9),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_ignores_unknown_fragments():
-    expect_passes_rule(
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-      field
-      ...Unknown
-      ...Known
-    }
-
-    fragment Known on T {
-      field
-      ...OtherUnknown
-    }
-    """,
-    )
-
-
-SomeBox = GraphQLInterfaceType(
-    "SomeBox",
-    fields=lambda: {
-        "deepBox": GraphQLField(SomeBox),
-        "unrelatedField": GraphQLField(GraphQLString),
-    },
-    resolve_type=lambda *_: StringBox,
-)
-
-StringBox = GraphQLObjectType(
-    "StringBox",
-    fields=lambda: {
-        "scalar": GraphQLField(GraphQLString),
-        "deepBox": GraphQLField(StringBox),
-        "unrelatedField": GraphQLField(GraphQLString),
-        "listStringBox": GraphQLField(GraphQLList(StringBox)),
-        "stringBox": GraphQLField(StringBox),
-        "intBox": GraphQLField(IntBox),
-    },
-    interfaces=[SomeBox],
-)
-
-IntBox = GraphQLObjectType(
-    "IntBox",
-    fields=lambda: {
-        "scalar": GraphQLField(GraphQLInt),
-        "deepBox": GraphQLField(IntBox),
-        "unrelatedField": GraphQLField(GraphQLString),
-        "listStringBox": GraphQLField(GraphQLList(StringBox)),
-        "stringBox": GraphQLField(StringBox),
-        "intBox": GraphQLField(IntBox),
-    },
-    interfaces=[SomeBox],
-)
-
-NonNullStringBox1 = GraphQLInterfaceType(
-    "NonNullStringBox1",
-    {"scalar": GraphQLField(GraphQLNonNull(GraphQLString))},
-    resolve_type=lambda *_: StringBox,
-)
-
-NonNullStringBox1Impl = GraphQLObjectType(
-    "NonNullStringBox1Impl",
-    {
-        "scalar": GraphQLField(GraphQLNonNull(GraphQLString)),
-        "deepBox": GraphQLField(StringBox),
-        "unrelatedField": GraphQLField(GraphQLString),
-    },
-    interfaces=[SomeBox, NonNullStringBox1],
-)
-
-NonNullStringBox2 = GraphQLInterfaceType(
-    "NonNullStringBox2",
-    {"scalar": GraphQLField(GraphQLNonNull(GraphQLString))},
-    resolve_type=lambda *_: StringBox,
-)
-
-NonNullStringBox2Impl = GraphQLObjectType(
-    "NonNullStringBox2Impl",
-    {
-        "scalar": GraphQLField(GraphQLNonNull(GraphQLString)),
-        "unrelatedField": GraphQLField(GraphQLString),
-        "deepBox": GraphQLField(StringBox),
-    },
-    interfaces=[SomeBox, NonNullStringBox2],
-)
-
-Connection = GraphQLObjectType(
-    "Connection",
-    {
-        "edges": GraphQLField(
-            GraphQLList(
-                GraphQLObjectType(
-                    "Edge",
-                    {
-                        "node": GraphQLField(
-                            GraphQLObjectType(
-                                "Node",
-                                {
-                                    "id": GraphQLField(GraphQLID),
-                                    "name": GraphQLField(GraphQLString),
-                                },
-                            )
-                        )
-                    },
-                )
-            )
-        )
-    },
-)
-
-schema = GraphQLSchema(
-    GraphQLObjectType(
-        "QueryRoot",
-        {"someBox": GraphQLField(SomeBox), "connection": GraphQLField(Connection)},
-    ),
-    types=[IntBox, StringBox, NonNullStringBox1Impl, NonNullStringBox2Impl],
-)
-
-
-def test_conflicting_return_types_which_potentially_overlap():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        someBox {
-            ...on IntBox {
-                scalar
-            }
-            ...on NonNullStringBox1 {
-                scalar
-            }
-        }
-    }
-
-    """,
-        [
-            fields_conflict(
-                "scalar",
-                "they return conflicting types Int and String!",
-                L(5, 17),
-                L(8, 17),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_compatible_return_shapes_on_different_return_types():
-    # In this case `deepBox` returns `SomeBox` in the first usage, and
-    # `StringBox` in the second usage. These return types are not the same!
-    # however this is valid because the return *shapes* are compatible.
-    expect_passes_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-      {
-        someBox {
-          ... on SomeBox {
-            deepBox {
-              unrelatedField
-            }
-          }
-          ... on StringBox {
-            deepBox {
-              unrelatedField
-            }
-          }
-        }
-      }
-    """,
-    )
-
-
-def test_disallows_differing_return_types_despite_no_overlap():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-        {
-          someBox {
-            ... on IntBox {
-              scalar
-            }
-            ... on StringBox {
-              scalar
-            }
-          }
-        }
-    """,
-        [
-            fields_conflict(
-                "scalar",
-                "they return conflicting types Int and String",
-                L(5, 15),
-                L(8, 15),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_reports_correctly_when_a_non_exclusive_follows_an_exclusive():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-        {
-          someBox {
-            ... on IntBox {
-              deepBox {
-                ...X
-              }
-            }
-          }
-          someBox {
-            ... on StringBox {
-              deepBox {
-                ...Y
-              }
-            }
-          }
-          memoed: someBox {
-            ... on IntBox {
-              deepBox {
-                ...X
-              }
-            }
-          }
-          memoed: someBox {
-            ... on StringBox {
-              deepBox {
-                ...Y
-              }
-            }
-          }
-          other: someBox {
-            ...X
-          }
-          other: someBox {
-            ...Y
-          }
-        }
-        fragment X on SomeBox {
-          scalar
-        }
-        fragment Y on SomeBox {
-          scalar: unrelatedField
-        }
-    """,
-        [
-            fields_conflict(
-                "other",
-                [("scalar", "scalar and unrelatedField are different fields")],
-                L(31, 11),
-                L(39, 11),
-                L(34, 11),
-                L(42, 11),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_disallows_differing_return_type_nullability_despite_no_overlap():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-        {
-          someBox {
-            ... on NonNullStringBox1 {
-              scalar
-            }
-            ... on StringBox {
-              scalar
-            }
-          }
-        }
-    """,
-        [
-            fields_conflict(
-                "scalar",
-                "they return conflicting types String! and String",
-                L(5, 15),
-                L(8, 15),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_disallows_differing_return_type_list_despite_no_overlap_1():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-        {
-          someBox {
-            ... on IntBox {
-              box: listStringBox {
-                scalar
-              }
-            }
-            ... on StringBox {
-              box: stringBox {
-                scalar
-              }
-            }
-          }
-        }
-    """,
-        [
-            fields_conflict(
-                "box",
-                "they return conflicting types [StringBox] and StringBox",
-                L(5, 15),
-                L(10, 15),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_disallows_differing_return_type_list_despite_no_overlap_2():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-        {
-          someBox {
-            ... on IntBox {
-              box: stringBox {
-                scalar
-              }
-            }
-            ... on StringBox {
-              box: listStringBox {
-                scalar
-              }
-            }
-          }
-        }
-    """,
-        [
-            fields_conflict(
-                "box",
-                "they return conflicting types StringBox and [StringBox]",
-                L(5, 15),
-                L(10, 15),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_disallows_differing_subfields():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-        {
-          someBox {
-            ... on IntBox {
-              box: stringBox {
-                val: scalar
-                val: unrelatedField
-              }
-            }
-            ... on StringBox {
-              box: stringBox {
-                val: scalar
-              }
-            }
-          }
-        }
-    """,
-        [
-            fields_conflict(
-                "val",
-                "scalar and unrelatedField are different fields",
-                L(6, 17),
-                L(7, 17),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_disallows_differing_deep_return_types_despite_no_overlap():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-        {
-          someBox {
-            ... on IntBox {
-              box: stringBox {
-                scalar
-              }
-            }
-            ... on StringBox {
-              box: intBox {
-                scalar
-              }
-            }
-          }
-        }
-    """,
-        [
-            fields_conflict(
-                "box",
-                [["scalar", "they return conflicting types String and Int"]],
-                L(5, 15),
-                L(6, 17),
-                L(10, 15),
-                L(11, 17),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_allows_non_conflicting_overlaping_types():
-    expect_passes_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-        {
-          someBox {
-            ... on IntBox {
-              scalar: unrelatedField
-            }
-            ... on StringBox {
-              scalar
-            }
-          }
-        }
-    """,
-    )
-
-
-def test_same_wrapped_scalar_return_types():
-    expect_passes_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        someBox {
-            ...on NonNullStringBox1 {
-              scalar
-            }
-            ...on NonNullStringBox2 {
-              scalar
-            }
-        }
-    }
-    """,
-    )
-
-
-def test_allows_inline_typeless_fragments():
-    expect_passes_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        a
-        ... {
-            a
-        }
-    }
-    """,
-    )
-
-
-def test_compares_deep_types_including_list():
-    expect_fails_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        connection {
-            ...edgeID
-            edges {
-                node {
-                    id: name
-                }
-            }
-        }
-    }
-
-    fragment edgeID on Connection {
-        edges {
-            node {
-                id
-            }
-        }
-    }
-    """,
-        [
-            fields_conflict(
-                "edges",
-                [["node", [["id", "name and id are different fields"]]]],
-                L(5, 13),
-                L(6, 17),
-                L(7, 21),
-                L(14, 9),
-                L(15, 13),
-                L(16, 17),
-            )
-        ],
-        sort_list=False,
-    )
-
-
-def test_ignores_unknown_types():
-    expect_passes_rule_with_schema(
-        schema,
-        OverlappingFieldsCanBeMerged,
-        """
-    {
-        boxUnion {
-            ...on UnknownType {
-                scalar
-            }
-            ...on NonNullStringBox2 {
-                scalar
-            }
-        }
-    }
-    """,
-    )
-
-
-def test_error_message_contains_hint_for_alias_conflict():
-    error = OverlappingFieldsCanBeMerged.fields_conflict_message(
-        "x", "a and b are different fields"
-    )
-    hint = (
-        'Fields "x" conflict because a and b are different fields. '
-        "Use different aliases on the fields to fetch both "
-        "if this was intentional."
-    )
-    assert error == hint
diff --git a/graphql/validation/tests/test_possible_fragment_spreads.py b/graphql/validation/tests/test_possible_fragment_spreads.py
deleted file mode 100644
index 2bac5b1..0000000
--- a/graphql/validation/tests/test_possible_fragment_spreads.py
+++ /dev/null
@@ -1,255 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import PossibleFragmentSpreads
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def error(frag_name, parent_type, frag_type, line, column):
-    return {
-        "message": PossibleFragmentSpreads.type_incompatible_spread_message(
-            frag_name, parent_type, frag_type
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def error_anon(parent_type, frag_type, line, column):
-    return {
-        "message": PossibleFragmentSpreads.type_incompatible_anon_spread_message(
-            parent_type, frag_type
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_same_object():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment objectWithinObject on Dog { ...dogFragment }
-      fragment dogFragment on Dog { barkVolume }
-    """,
-    )
-
-
-def test_same_object_inline_frag():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } }
-    """,
-    )
-
-
-def test_object_into_implemented_interface():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment objectWithinInterface on Pet { ...dogFragment }
-      fragment dogFragment on Dog { barkVolume }
-    """,
-    )
-
-
-def test_object_into_containing_union():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment objectWithinUnion on CatOrDog { ...dogFragment }
-      fragment dogFragment on Dog { barkVolume }
-    """,
-    )
-
-
-def test_union_into_contained_object():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment unionWithinObject on Dog { ...catOrDogFragment }
-      fragment catOrDogFragment on CatOrDog { __typename }
-    """,
-    )
-
-
-def test_union_into_overlapping_interface():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment unionWithinInterface on Pet { ...catOrDogFragment }
-      fragment catOrDogFragment on CatOrDog { __typename }
-    """,
-    )
-
-
-def test_union_into_overlapping_union():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment }
-      fragment catOrDogFragment on CatOrDog { __typename }
-    """,
-    )
-
-
-def test_interface_into_implemented_object():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment interfaceWithinObject on Dog { ...petFragment }
-      fragment petFragment on Pet { name }
-    """,
-    )
-
-
-def test_interface_into_overlapping_interface():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment interfaceWithinInterface on Pet { ...beingFragment }
-      fragment beingFragment on Being { name }
-    """,
-    )
-
-
-def test_interface_into_overlapping_interface_in_inline_fragment():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment interfaceWithinInterface on Pet { ... on Being { name } }
-    """,
-    )
-
-
-def test_interface_into_overlapping_union():
-    expect_passes_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment interfaceWithinUnion on CatOrDog { ...petFragment }
-      fragment petFragment on Pet { name }
-    """,
-    )
-
-
-def test_different_object_into_object():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidObjectWithinObject on Cat { ...dogFragment }
-      fragment dogFragment on Dog { barkVolume }
-    """,
-        [error("dogFragment", "Cat", "Dog", 2, 51)],
-    )
-
-
-def test_different_object_into_object_in_inline_fragment():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidObjectWithinObjectAnon on Cat {
-        ... on Dog { barkVolume }
-      }
-    """,
-        [error_anon("Cat", "Dog", 3, 9)],
-    )
-
-
-def test_object_into_not_implementing_interface():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidObjectWithinInterface on Pet { ...humanFragment }
-      fragment humanFragment on Human { pets { name } }
-    """,
-        [error("humanFragment", "Pet", "Human", 2, 54)],
-    )
-
-
-def test_object_into_not_containing_union():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment }
-      fragment humanFragment on Human { pets { name } }
-    """,
-        [error("humanFragment", "CatOrDog", "Human", 2, 55)],
-    )
-
-
-def test_union_into_not_contained_object():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidUnionWithinObject on Human { ...catOrDogFragment }
-      fragment catOrDogFragment on CatOrDog { __typename }
-    """,
-        [error("catOrDogFragment", "Human", "CatOrDog", 2, 52)],
-    )
-
-
-def test_union_into_non_overlapping_interface():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment }
-      fragment humanOrAlienFragment on HumanOrAlien { __typename }
-    """,
-        [error("humanOrAlienFragment", "Pet", "HumanOrAlien", 2, 53)],
-    )
-
-
-def test_union_into_non_overlapping_union():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment }
-      fragment humanOrAlienFragment on HumanOrAlien { __typename }
-    """,
-        [error("humanOrAlienFragment", "CatOrDog", "HumanOrAlien", 2, 54)],
-    )
-
-
-def test_interface_into_non_implementing_object():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment }
-      fragment intelligentFragment on Intelligent { iq }
-    """,
-        [error("intelligentFragment", "Cat", "Intelligent", 2, 54)],
-    )
-
-
-def test_interface_into_non_overlapping_interface():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidInterfaceWithinInterface on Pet {
-        ...intelligentFragment
-      }
-      fragment intelligentFragment on Intelligent { iq }
-    """,
-        [error("intelligentFragment", "Pet", "Intelligent", 3, 9)],
-    )
-
-
-def test_interface_into_non_overlapping_interface_in_inline_fragment():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidInterfaceWithinInterfaceAnon on Pet {
-        ...on Intelligent { iq }
-      }
-    """,
-        [error_anon("Pet", "Intelligent", 3, 9)],
-    )
-
-
-def test_interface_into_non_overlapping_union():
-    expect_fails_rule(
-        PossibleFragmentSpreads,
-        """
-      fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment }
-      fragment petFragment on Pet { name }
-    """,
-        [error("petFragment", "HumanOrAlien", "Pet", 2, 62)],
-    )
diff --git a/graphql/validation/tests/test_provided_non_null_arguments.py b/graphql/validation/tests/test_provided_non_null_arguments.py
deleted file mode 100644
index 2727207..0000000
--- a/graphql/validation/tests/test_provided_non_null_arguments.py
+++ /dev/null
@@ -1,251 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import ProvidedNonNullArguments
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def missing_field_arg(field_name, arg_name, type_name, line, column):
-    return {
-        "message": ProvidedNonNullArguments.missing_field_arg_message(
-            field_name, arg_name, type_name
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def missing_directive_arg(directive_name, arg_name, type_name, line, column):
-    return {
-        "message": ProvidedNonNullArguments.missing_directive_arg_message(
-            directive_name, arg_name, type_name
-        ),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_ignores_unknown_arguments():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        dog {
-          isHousetrained(unknownArgument: true)
-        }
-    }""",
-    )
-
-
-def test_arg_on_optional_arg():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        dog {
-          isHousetrained(atOtherHomes: true)
-        }
-    }""",
-    )
-
-
-def test_no_arg_on_optional_arg():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        dog {
-          isHousetrained
-        }
-    }""",
-    )
-
-
-def test_multiple_args():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleReqs(req1: 1, req2: 2)
-        }
-    }
-    """,
-    )
-
-
-def test_multiple_args_reverse_order():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleReqs(req2: 2, req1: 1)
-        }
-    }
-    """,
-    )
-
-
-def test_no_args_on_multiple_optional():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleOpts
-        }
-    }
-    """,
-    )
-
-
-def test_one_arg_on_multiple_optional():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleOpts(opt1: 1)
-        }
-    }
-    """,
-    )
-
-
-def test_second_arg_on_multiple_optional():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleOpts(opt2: 1)
-        }
-    }
-    """,
-    )
-
-
-def test_multiple_reqs_on_mixed_list():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleOptAndReq(req1: 3, req2: 4)
-        }
-    }
-    """,
-    )
-
-
-def test_multiple_reqs_and_one_opt_on_mixed_list():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
-        }
-    }
-    """,
-    )
-
-
-def test_all_reqs_and_opts_on_mixed_list():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
-        }
-    }
-    """,
-    )
-
-
-def test_missing_one_non_nullable_argument():
-    expect_fails_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleReqs(req2: 2)
-        }
-    }
-    """,
-        [missing_field_arg("multipleReqs", "req1", "Int!", 4, 13)],
-    )
-
-
-def test_missing_multiple_non_nullable_arguments():
-    expect_fails_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleReqs
-        }
-    }
-    """,
-        [
-            missing_field_arg("multipleReqs", "req1", "Int!", 4, 13),
-            missing_field_arg("multipleReqs", "req2", "Int!", 4, 13),
-        ],
-    )
-
-
-def test_incorrect_value_and_missing_argument():
-    expect_fails_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        complicatedArgs {
-            multipleReqs(req1: "one")
-        }
-    }
-    """,
-        [missing_field_arg("multipleReqs", "req2", "Int!", 4, 13)],
-    )
-
-
-def test_ignore_unknown_directives():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        dog @unknown
-    }
-    """,
-    )
-
-
-def test_with_directives_of_valid_type():
-    expect_passes_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        dog @include(if: true) {
-            name
-        }
-        human @skip(if: false) {
-            name
-        }
-    }
-    """,
-    )
-
-
-def test_with_directive_with_missing_types():
-    expect_fails_rule(
-        ProvidedNonNullArguments,
-        """
-    {
-        dog @include {
-            name @skip
-        }
-    }
-    """,
-        [
-            missing_directive_arg("include", "if", "Boolean!", 3, 13),
-            missing_directive_arg("skip", "if", "Boolean!", 4, 18),
-        ],
-    )
diff --git a/graphql/validation/tests/test_scalar_leafs.py b/graphql/validation/tests/test_scalar_leafs.py
deleted file mode 100644
index f381c73..0000000
--- a/graphql/validation/tests/test_scalar_leafs.py
+++ /dev/null
@@ -1,124 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import ScalarLeafs
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def no_scalar_subselection(field, type, line, column):
-    return {
-        "message": ScalarLeafs.no_subselection_allowed_message(field, type),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def missing_obj_subselection(field, type, line, column):
-    return {
-        "message": ScalarLeafs.required_subselection_message(field, type),
-        "locations": [SourceLocation(line, column)],
-    }
-
-
-def test_valid_scalar_selection():
-    expect_passes_rule(
-        ScalarLeafs,
-        """
-      fragment scalarSelection on Dog {
-        barks
-      }
-    """,
-    )
-
-
-def test_object_type_missing_selection():
-    expect_fails_rule(
-        ScalarLeafs,
-        """
-      query directQueryOnObjectWithoutSubFields {
-        human
-      }
-    """,
-        [missing_obj_subselection("human", "Human", 3, 9)],
-    )
-
-
-def test_interface_type_missing_selection():
-    expect_fails_rule(
-        ScalarLeafs,
-        """
-      {
-        human { pets }
-      }
-    """,
-        [missing_obj_subselection("pets", "[Pet]", 3, 17)],
-    )
-
-
-def test_valid_scalar_selection_with_args():
-    expect_passes_rule(
-        ScalarLeafs,
-        """
-      fragment scalarSelectionWithArgs on Dog {
-        doesKnowCommand(dogCommand: SIT)
-      }
-    """,
-    )
-
-
-def test_scalar_selection_not_allowed_on_boolean():
-    expect_fails_rule(
-        ScalarLeafs,
-        """
-      fragment scalarSelectionsNotAllowedOnBoolean on Dog {
-        barks { sinceWhen }
-      }
-    """,
-        [no_scalar_subselection("barks", "Boolean", 3, 15)],
-    )
-
-
-def test_scalar_selection_not_allowed_on_enum():
-    expect_fails_rule(
-        ScalarLeafs,
-        """
-      fragment scalarSelectionsNotAllowedOnEnum on Cat {
-        furColor { inHexdec }
-      }
-    """,
-        [no_scalar_subselection("furColor", "FurColor", 3, 18)],
-    )
-
-
-def test_scalar_selection_not_allowed_with_args():
-    expect_fails_rule(
-        ScalarLeafs,
-        """
-      fragment scalarSelectionsNotAllowedWithArgs on Dog {
-        doesKnowCommand(dogCommand: SIT) { sinceWhen }
-      }
-    """,
-        [no_scalar_subselection("doesKnowCommand", "Boolean", 3, 42)],
-    )
-
-
-def test_scalar_selection_not_allowed_with_directives():
-    expect_fails_rule(
-        ScalarLeafs,
-        """
-      fragment scalarSelectionsNotAllowedWithDirectives on Dog {
-        name @include(if: true) { isAlsoHumanName }
-      }
-    """,
-        [no_scalar_subselection("name", "String", 3, 33)],
-    )
-
-
-def test_scalar_selection_not_allowed_with_directives_and_args():
-    expect_fails_rule(
-        ScalarLeafs,
-        """
-      fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog {
-        doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen }
-      }
-    """,
-        [no_scalar_subselection("doesKnowCommand", "Boolean", 3, 61)],
-    )
diff --git a/graphql/validation/tests/test_unique_argument_names.py b/graphql/validation/tests/test_unique_argument_names.py
deleted file mode 100644
index 57b1815..0000000
--- a/graphql/validation/tests/test_unique_argument_names.py
+++ /dev/null
@@ -1,159 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import UniqueArgumentNames
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def duplicate_arg(arg_name, l1, c1, l2, c2):
-    return {
-        "message": UniqueArgumentNames.duplicate_arg_message(arg_name),
-        "locations": [SourceLocation(l1, c1), SourceLocation(l2, c2)],
-    }
-
-
-def test_no_arguments_on_field():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-      {
-        field
-      }
-    """,
-    )
-
-
-def test_no_arguments_on_directive():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-      {
-        field
-      }
-    """,
-    )
-
-
-def test_argument_on_field():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-      {
-        field(arg: "value")
-      }
-    """,
-    )
-
-
-def test_argument_on_directive():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-       {
-        field @directive(arg: "value")
-      }
-    """,
-    )
-
-
-def test_same_field_two_arguments():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-      {
-        one: field(arg: "value")
-        two: field(arg: "value")
-      }
-    """,
-    )
-
-
-def test_same_argument_on_field_and_directive():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-      {
-        field(arg: "value") @directive(arg: "value")
-      }
-    """,
-    )
-
-
-def test_same_argument_two_directives():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-      {
-        field @directive1(arg: "value") @directive2(arg: "value")
-      }
-    """,
-    )
-
-
-def test_multiple_field_arguments():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-      {
-        field(arg1: "value", arg2: "value", arg3: "value")
-      }
-    """,
-    )
-
-
-def test_multiple_directive_arguments():
-    expect_passes_rule(
-        UniqueArgumentNames,
-        """
-    {
-      field @directive(arg1: "value", arg2: "value", arg3: "value")
-    }
-    """,
-    )
-
-
-def test_duplicate_field_arguments():
-    expect_fails_rule(
-        UniqueArgumentNames,
-        """
-    {
-      field(arg1: "value", arg1: "value")
-    }
-    """,
-        [duplicate_arg("arg1", 3, 13, 3, 28)],
-    )
-
-
-def test_many_duplicate_field_arguments():
-    expect_fails_rule(
-        UniqueArgumentNames,
-        """
-    {
-      field(arg1: "value", arg1: "value", arg1: "value")
-    }
-    """,
-        [duplicate_arg("arg1", 3, 13, 3, 28), duplicate_arg("arg1", 3, 13, 3, 43)],
-    )
-
-
-def test_duplicate_directive_arguments():
-    expect_fails_rule(
-        UniqueArgumentNames,
-        """
-    {
-      field @directive(arg1: "value", arg1: "value")
-    }
-    """,
-        [duplicate_arg("arg1", 3, 24, 3, 39)],
-    )
-
-
-def test_many_duplicate_directive_arguments():
-    expect_fails_rule(
-        UniqueArgumentNames,
-        """
-    {
-      field @directive(arg1: "value", arg1: "value", arg1: "value")
-    }
-    """,
-        [duplicate_arg("arg1", 3, 24, 3, 39), duplicate_arg("arg1", 3, 24, 3, 54)],
-    )
diff --git a/graphql/validation/tests/test_unique_fragment_names.py b/graphql/validation/tests/test_unique_fragment_names.py
deleted file mode 100644
index 04cfdbb..0000000
--- a/graphql/validation/tests/test_unique_fragment_names.py
+++ /dev/null
@@ -1,121 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import UniqueFragmentNames
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def duplicate_fragment(fragment_name, l1, c1, l2, c2):
-    return {
-        "message": UniqueFragmentNames.duplicate_fragment_name_message(fragment_name),
-        "locations": [SourceLocation(l1, c1), SourceLocation(l2, c2)],
-    }
-
-
-def test_no_fragments():
-    expect_passes_rule(
-        UniqueFragmentNames,
-        """
-      {
-        field
-      }
-    """,
-    )
-
-
-def test_one_fragment():
-    expect_passes_rule(
-        UniqueFragmentNames,
-        """
-      {
-        ...fragA
-      }
-      fragment fragA on Type {
-        field
-      }
-    """,
-    )
-
-
-def test_many_fragments():
-    expect_passes_rule(
-        UniqueFragmentNames,
-        """
-      {
-        ...fragA
-        ...fragB
-        ...fragC
-      }
-      fragment fragA on Type {
-        fieldA
-      }
-      fragment fragB on Type {
-        fieldB
-      }
-      fragment fragC on Type {
-        fieldC
-      }
-    """,
-    )
-
-
-def test_inline_fragments():
-    expect_passes_rule(
-        UniqueFragmentNames,
-        """
-      {
-        ...on Type {
-          fieldA
-        }
-        ...on Type {
-          fieldB
-        }
-      }
-    """,
-    )
-
-
-def test_fragment_operation_same_name():
-    expect_passes_rule(
-        UniqueFragmentNames,
-        """
-      query Foo {
-        ...Foo
-      }
-      fragment Foo on Type {
-        field
-      }
-    """,
-    )
-
-
-def test_fragments_same_name():
-    expect_fails_rule(
-        UniqueFragmentNames,
-        """
-        {
-          ...fragA
-        }
-        fragment fragA on Type {
-          fieldA
-        }
-        fragment fragA on Type {
-          fieldB
-        }
-    """,
-        [duplicate_fragment("fragA", 5, 18, 8, 18)],
-    )
-
-
-def test_fragments_same_name_no_ref():
-    expect_fails_rule(
-        UniqueFragmentNames,
-        """
-        fragment fragA on Type {
-          fieldA
-        }
-        fragment fragA on Type {
-          fieldB
-        }
-    """,
-        [duplicate_fragment("fragA", 2, 18, 5, 18)],
-    )
diff --git a/graphql/validation/tests/test_unique_input_field_names.py b/graphql/validation/tests/test_unique_input_field_names.py
deleted file mode 100644
index 6587ed0..0000000
--- a/graphql/validation/tests/test_unique_input_field_names.py
+++ /dev/null
@@ -1,90 +0,0 @@
-from graphql.language.location import SourceLocation as L
-from graphql.validation.rules import UniqueInputFieldNames
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def duplicate_field(name, l1, l2):
-    return {
-        "message": UniqueInputFieldNames.duplicate_input_field_message(name),
-        "locations": [l1, l2],
-    }
-
-
-def test_input_object_with_fields():
-    expect_passes_rule(
-        UniqueInputFieldNames,
-        """
-    {
-        field(arg: { f: true })
-    }
-    """,
-    )
-
-
-def test_same_input_object_within_two_args():
-    expect_passes_rule(
-        UniqueInputFieldNames,
-        """
-    {
-        field(arg1: { f: true }, arg2: { f: true })
-    }
-    """,
-    )
-
-
-def test_multiple_input_object_fields():
-    expect_passes_rule(
-        UniqueInputFieldNames,
-        """
-    {
-        field(arg: { f1: "value", f2: "value", f3: "value" })
-    }
-    """,
-    )
-
-
-def test_it_allows_for_nested_input_objects_with_similar_fields():
-    expect_passes_rule(
-        UniqueInputFieldNames,
-        """
-    {
-        field(arg: {
-            deep: {
-              deep: {
-                id: 1
-            }
-            id: 1
-            }
-            id: 1
-        })
-    }
-    """,
-    )
-
-
-def test_duplicate_input_object_fields():
-    expect_fails_rule(
-        UniqueInputFieldNames,
-        """
-    {
-        field(arg: { f1: "value", f1: "value" })
-    }
-    """,
-        [duplicate_field("f1", L(3, 22), L(3, 35))],
-    )
-
-
-def test_many_duplicate_input_object_fields():
-    expect_fails_rule(
-        UniqueInputFieldNames,
-        """
-    {
-        field(arg: { f1: "value", f1: "value", f1: "value" })
-    }
-    """,
-        [
-            duplicate_field("f1", L(3, 22), L(3, 35)),
-            duplicate_field("f1", L(3, 22), L(3, 48)),
-        ],
-    )
diff --git a/graphql/validation/tests/test_unique_operation_names.py b/graphql/validation/tests/test_unique_operation_names.py
deleted file mode 100644
index 9eac02b..0000000
--- a/graphql/validation/tests/test_unique_operation_names.py
+++ /dev/null
@@ -1,137 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import UniqueOperationNames
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def duplicate_op(op_name, l1, c1, l2, c2):
-    return {
-        "message": UniqueOperationNames.duplicate_operation_name_message(op_name),
-        "locations": [SourceLocation(l1, c1), SourceLocation(l2, c2)],
-    }
-
-
-def test_no_operations():
-    expect_passes_rule(
-        UniqueOperationNames,
-        """
-      fragment fragA on Type {
-        field
-      }
-    """,
-    )
-
-
-def test_one_anon_operation():
-    expect_passes_rule(
-        UniqueOperationNames,
-        """
-      {
-        field
-      }
-    """,
-    )
-
-
-def test_one_named_operation():
-    expect_passes_rule(
-        UniqueOperationNames,
-        """
-      query Foo {
-        field
-      }
-    """,
-    )
-
-
-def test_multiple_operations():
-    expect_passes_rule(
-        UniqueOperationNames,
-        """
-      query Foo {
-        field
-      }
-
-      query Bar {
-        field
-      }
-    """,
-    )
-
-
-def test_multiple_operations_of_different_types():
-    expect_passes_rule(
-        UniqueOperationNames,
-        """
-      query Foo {
-        field
-      }
-
-      mutation Bar {
-        field
-      }
-
-      subscription Baz {
-        field
-      }
-    """,
-    )
-
-
-def test_fragment_and_operation_named_the_same():
-    expect_passes_rule(
-        UniqueOperationNames,
-        """
-      query Foo {
-        ...Foo
-      }
-      fragment Foo on Type {
-        field
-      }
-    """,
-    )
-
-
-def test_multiple_operations_of_same_name():
-    expect_fails_rule(
-        UniqueOperationNames,
-        """
-      query Foo {
-        fieldA
-      }
-      query Foo {
-        fieldB
-      }
-    """,
-        [duplicate_op("Foo", 2, 13, 5, 13)],
-    )
-
-
-def test_multiple_ops_of_same_name_of_different_types_mutation():
-    expect_fails_rule(
-        UniqueOperationNames,
-        """
-      query Foo {
-        fieldA
-      }
-      mutation Foo {
-        fieldB
-      }
-    """,
-        [duplicate_op("Foo", 2, 13, 5, 16)],
-    )
-
-
-def test_multiple_ops_of_same_name_of_different_types_subscription():
-    expect_fails_rule(
-        UniqueOperationNames,
-        """
-      query Foo {
-        fieldA
-      }
-      subscription Foo {
-        fieldB
-      }
-    """,
-        [duplicate_op("Foo", 2, 13, 5, 20)],
-    )
diff --git a/graphql/validation/tests/test_unique_variable_names.py b/graphql/validation/tests/test_unique_variable_names.py
deleted file mode 100644
index c8e14af..0000000
--- a/graphql/validation/tests/test_unique_variable_names.py
+++ /dev/null
@@ -1,38 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import UniqueVariableNames
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def duplicate_var(op_name, l1, c1, l2, c2):
-    return {
-        "message": UniqueVariableNames.duplicate_variable_message(op_name),
-        "locations": [SourceLocation(l1, c1), SourceLocation(l2, c2)],
-    }
-
-
-def test_unique_variable_names():
-    expect_passes_rule(
-        UniqueVariableNames,
-        """
-      query A($x: Int, $y: String) { __typename }
-      query B($x: String, $y: Int) { __typename }
-    """,
-    )
-
-
-def test_duplicate_variable_names():
-    expect_fails_rule(
-        UniqueVariableNames,
-        """
-      query A($x: Int, $x: Int, $x: String) { __typename }
-      query B($x: String, $x: Int) { __typename }
-      query C($x: Int, $x: Int) { __typename }
-    """,
-        [
-            duplicate_var("x", 2, 16, 2, 25),
-            duplicate_var("x", 2, 16, 2, 34),
-            duplicate_var("x", 3, 16, 3, 28),
-            duplicate_var("x", 4, 16, 4, 25),
-        ],
-    )
diff --git a/graphql/validation/tests/test_validation.py b/graphql/validation/tests/test_validation.py
deleted file mode 100644
index 22f9607..0000000
--- a/graphql/validation/tests/test_validation.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from graphql import parse, validate
-from graphql.utils.type_info import TypeInfo
-from graphql.validation.rules import specified_rules
-from graphql.validation.validation import visit_using_rules
-
-from .utils import test_schema
-
-
-def expect_valid(schema, query_string):
-    errors = validate(schema, parse(query_string))
-    assert not errors
-
-
-def test_it_validates_queries():
-    expect_valid(
-        test_schema,
-        """
-      query {
-        catOrDog {
-          ... on Cat {
-            furColor
-          }
-          ... on Dog {
-            isHousetrained
-          }
-        }
-      }
-    """,
-    )
-
-
-def test_validates_using_a_custom_type_info():
-    type_info = TypeInfo(test_schema, lambda *_: None)
-
-    ast = parse(
-        """
-      query {
-        catOrDog {
-          ... on Cat {
-            furColor
-          }
-          ... on Dog {
-            isHousetrained
-          }
-        }
-      }
-    """
-    )
-
-    errors = visit_using_rules(test_schema, type_info, ast, specified_rules)
-
-    assert len(errors) == 3
-    assert (
-        errors[0].message
-        == 'Cannot query field "catOrDog" on type "QueryRoot". Did you mean "catOrDog"?'
-    )
-    assert (
-        errors[1].message
-        == 'Cannot query field "furColor" on type "Cat". Did you mean "furColor"?'
-    )
-    assert (
-        errors[2].message
-        == 'Cannot query field "isHousetrained" on type "Dog". Did you mean "isHousetrained"?'
-    )
diff --git a/graphql/validation/tests/test_variables_are_input_types.py b/graphql/validation/tests/test_variables_are_input_types.py
deleted file mode 100644
index a9daea3..0000000
--- a/graphql/validation/tests/test_variables_are_input_types.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import VariablesAreInputTypes
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def non_input_type_on_variable(variable_name, type_name, line, col):
-    return {
-        "message": VariablesAreInputTypes.non_input_type_on_variable_message(
-            variable_name, type_name
-        ),
-        "locations": [SourceLocation(line, col)],
-    }
-
-
-def test_input_types_are_valid():
-    expect_passes_rule(
-        VariablesAreInputTypes,
-        """
-      query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) {
-        field(a: $a, b: $b, c: $c)
-      }
-    """,
-    )
-
-
-def test_output_types_are_invalid():
-    expect_fails_rule(
-        VariablesAreInputTypes,
-        """
-      query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) {
-        field(a: $a, b: $b, c: $c)
-      }
-    """,
-        [
-            non_input_type_on_variable("a", "Dog", 2, 21),
-            non_input_type_on_variable("b", "[[CatOrDog!]]!", 2, 30),
-            non_input_type_on_variable("c", "Pet", 2, 50),
-        ],
-    )
diff --git a/graphql/validation/tests/test_variables_in_allowed_position.py b/graphql/validation/tests/test_variables_in_allowed_position.py
deleted file mode 100644
index ffcb0ff..0000000
--- a/graphql/validation/tests/test_variables_in_allowed_position.py
+++ /dev/null
@@ -1,356 +0,0 @@
-from graphql.language.location import SourceLocation
-from graphql.validation.rules import VariablesInAllowedPosition
-
-from .utils import expect_fails_rule, expect_passes_rule
-
-
-def test_boolean_boolean():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($booleanArg: Boolean)
-      {
-        complicatedArgs {
-          booleanArgField(booleanArg: $booleanArg)
-        }
-      }
-    """,
-    )
-
-
-def test_boolean_boolean_in_fragment():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      fragment booleanArgFrag on ComplicatedArgs {
-        booleanArgField(booleanArg: $booleanArg)
-      }
-
-      query Query($booleanArg: Boolean)
-      {
-        complicatedArgs {
-          ...booleanArgFrag
-        }
-      }
-    """,
-    )
-
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($booleanArg: Boolean)
-      {
-        complicatedArgs {
-          ...booleanArgFrag
-        }
-      }
-      fragment booleanArgFrag on ComplicatedArgs {
-        booleanArgField(booleanArg: $booleanArg)
-      }
-    """,
-    )
-
-
-def test_non_null_boolean_boolean():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($nonNullBooleanArg: Boolean!)
-      {
-        complicatedArgs {
-          booleanArgField(booleanArg: $nonNullBooleanArg)
-        }
-      }
-    """,
-    )
-
-
-def test_non_null_boolean_to_boolean_within_fragment():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      fragment booleanArgFrag on ComplicatedArgs {
-        booleanArgField(booleanArg: $nonNullBooleanArg)
-      }
-      query Query($nonNullBooleanArg: Boolean!)
-      {
-        complicatedArgs {
-          ...booleanArgFrag
-        }
-      }
-    """,
-    )
-
-
-def test_int_non_null_int_with_default():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($intArg: Int = 1)
-      {
-        complicatedArgs {
-          nonNullIntArgField(nonNullIntArg: $intArg)
-        }
-      }
-    """,
-    )
-
-
-def test_string_string():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($stringListVar: [String])
-      {
-        complicatedArgs {
-          stringListArgField(stringListArg: $stringListVar)
-        }
-      }
-    """,
-    )
-
-
-def test_non_null_string_string():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($stringListVar: [String!])
-      {
-        complicatedArgs {
-          stringListArgField(stringListArg: $stringListVar)
-        }
-      }
-    """,
-    )
-
-
-def test_string_string_item_position():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($stringVar: String)
-      {
-        complicatedArgs {
-          stringListArgField(stringListArg: [$stringVar])
-        }
-      }
-    """,
-    )
-
-
-def test_non_null_string_string_item_positiion():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($stringVar: String!)
-      {
-        complicatedArgs {
-          stringListArgField(stringListArg: [$stringVar])
-        }
-      }
-    """,
-    )
-
-
-def test_complex_input_complex_input():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($complexVar: ComplexInput)
-      {
-        complicatedArgs {
-          complexArgField(complexArg: $complexVar)
-        }
-      }
-    """,
-    )
-
-
-def test_complex_input_complex_input_in_field_position():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($boolVar: Boolean = false)
-      {
-        complicatedArgs {
-          complexArgField(complexArg: {requiredArg: $boolVar})
-        }
-      }
-    """,
-    )
-
-
-def test_boolean_non_null_boolean_in_directive():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($boolVar: Boolean!)
-      {
-        dog @include(if: $boolVar)
-      }
-    """,
-    )
-
-
-def test_boolean_non_null_boolean_in_directive_with_default():
-    expect_passes_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($boolVar: Boolean = false)
-      {
-        dog @include(if: $boolVar)
-      }
-    """,
-    )
-
-
-def test_int_non_null_int():
-    expect_fails_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($intArg: Int) {
-        complicatedArgs {
-          nonNullIntArgField(nonNullIntArg: $intArg)
-        }
-      }
-    """,
-        [
-            {
-                "message": VariablesInAllowedPosition.bad_var_pos_message(
-                    "intArg", "Int", "Int!"
-                ),
-                "locations": [SourceLocation(4, 45), SourceLocation(2, 19)],
-            }
-        ],
-    )
-
-
-def test_int_non_null_int_within_fragment():
-    expect_fails_rule(
-        VariablesInAllowedPosition,
-        """
-      fragment nonNullIntArgFieldFrag on ComplicatedArgs {
-        nonNullIntArgField(nonNullIntArg: $intArg)
-      }
-      query Query($intArg: Int) {
-        complicatedArgs {
-          ...nonNullIntArgFieldFrag
-        }
-      }
-    """,
-        [
-            {
-                "message": VariablesInAllowedPosition.bad_var_pos_message(
-                    "intArg", "Int", "Int!"
-                ),
-                "locations": [SourceLocation(5, 19), SourceLocation(3, 43)],
-            }
-        ],
-    )
-
-
-def test_int_non_null_int_within_nested_fragment():
-    expect_fails_rule(
-        VariablesInAllowedPosition,
-        """
-      fragment outerFrag on ComplicatedArgs {
-        ...nonNullIntArgFieldFrag
-      }
-      fragment nonNullIntArgFieldFrag on ComplicatedArgs {
-        nonNullIntArgField(nonNullIntArg: $intArg)
-      }
-      query Query($intArg: Int) {
-        complicatedArgs {
-          ...outerFrag
-        }
-      }
-    """,
-        [
-            {
-                "message": VariablesInAllowedPosition.bad_var_pos_message(
-                    "intArg", "Int", "Int!"
-                ),
-                "locations": [SourceLocation(8, 19), SourceLocation(6, 43)],
-            }
-        ],
-    )
-
-
-def test_string_over_boolean():
-    expect_fails_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($stringVar: String) {
-        complicatedArgs {
-          booleanArgField(booleanArg: $stringVar)
-        }
-      }
-    """,
-        [
-            {
-                "message": VariablesInAllowedPosition.bad_var_pos_message(
-                    "stringVar", "String", "Boolean"
-                ),
-                "locations": [SourceLocation(2, 19), SourceLocation(4, 39)],
-            }
-        ],
-    )
-
-
-def test_string_string_fail():
-    expect_fails_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($stringVar: String) {
-        complicatedArgs {
-          stringListArgField(stringListArg: $stringVar)
-        }
-      }
-    """,
-        [
-            {
-                "message": VariablesInAllowedPosition.bad_var_pos_message(
-                    "stringVar", "String", "[String]"
-                ),
-                "locations": [SourceLocation(2, 19), SourceLocation(4, 45)],
-            }
-        ],
-    )
-
-
-def test_boolean_non_null_boolean_in_directive():
-    expect_fails_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($boolVar: Boolean) {
-        dog @include(if: $boolVar)
-      }
-    """,
-        [
-            {
-                "message": VariablesInAllowedPosition.bad_var_pos_message(
-                    "boolVar", "Boolean", "Boolean!"
-                ),
-                "locations": [SourceLocation(2, 19), SourceLocation(3, 26)],
-            }
-        ],
-    )
-
-
-def test_string_non_null_boolean_in_directive():
-    expect_fails_rule(
-        VariablesInAllowedPosition,
-        """
-      query Query($stringVar: String) {
-        dog @include(if: $stringVar)
-      }
-    """,
-        [
-            {
-                "message": VariablesInAllowedPosition.bad_var_pos_message(
-                    "stringVar", "String", "Boolean!"
-                ),
-                "locations": [SourceLocation(2, 19), SourceLocation(3, 26)],
-            }
-        ],
-    )
diff --git a/graphql/validation/tests/utils.py b/graphql/validation/tests/utils.py
deleted file mode 100644
index 7bec276..0000000
--- a/graphql/validation/tests/utils.py
+++ /dev/null
@@ -1,311 +0,0 @@
-from graphql.error import format_error
-from graphql.language.parser import parse
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLBoolean,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLFloat,
-    GraphQLID,
-    GraphQLInputObjectField,
-    GraphQLInputObjectType,
-    GraphQLInt,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-    GraphQLUnionType,
-)
-from graphql.type.directives import (
-    DirectiveLocation,
-    GraphQLDirective,
-    GraphQLIncludeDirective,
-    GraphQLSkipDirective,
-)
-from graphql.validation import validate
-
-Being = GraphQLInterfaceType(
-    "Being",
-    {"name": GraphQLField(GraphQLString, {"surname": GraphQLArgument(GraphQLBoolean)})},
-)
-
-Pet = GraphQLInterfaceType(
-    "Pet",
-    {"name": GraphQLField(GraphQLString, {"surname": GraphQLArgument(GraphQLBoolean)})},
-)
-
-Canine = GraphQLInterfaceType(
-    "Canine",
-    {"name": GraphQLField(GraphQLString, {"surname": GraphQLArgument(GraphQLBoolean)})},
-)
-
-DogCommand = GraphQLEnumType(
-    "DogCommand",
-    {
-        "SIT": GraphQLEnumValue(0),
-        "HEEL": GraphQLEnumValue(1),
-        "DOWN": GraphQLEnumValue(2),
-    },
-)
-
-Dog = GraphQLObjectType(
-    "Dog",
-    {
-        "name": GraphQLField(
-            GraphQLString, {"surname": GraphQLArgument(GraphQLBoolean)}
-        ),
-        "nickname": GraphQLField(GraphQLString),
-        "barkVolume": GraphQLField(GraphQLInt),
-        "barks": GraphQLField(GraphQLBoolean),
-        "doesKnowCommand": GraphQLField(
-            GraphQLBoolean, {"dogCommand": GraphQLArgument(DogCommand)}
-        ),
-        "isHousetrained": GraphQLField(
-            GraphQLBoolean,
-            args={"atOtherHomes": GraphQLArgument(GraphQLBoolean, default_value=True)},
-        ),
-        "isAtLocation": GraphQLField(
-            GraphQLBoolean,
-            args={"x": GraphQLArgument(GraphQLInt), "y": GraphQLArgument(GraphQLInt)},
-        ),
-    },
-    interfaces=[Being, Pet, Canine],
-    is_type_of=lambda: None,
-)
-
-Cat = GraphQLObjectType(
-    "Cat",
-    lambda: {
-        "furColor": GraphQLField(FurColor),
-        "name": GraphQLField(
-            GraphQLString, {"surname": GraphQLArgument(GraphQLBoolean)}
-        ),
-        "nickname": GraphQLField(GraphQLString),
-    },
-    interfaces=[Being, Pet],
-    is_type_of=lambda: None,
-)
-
-CatOrDog = GraphQLUnionType("CatOrDog", [Dog, Cat])
-
-Intelligent = GraphQLInterfaceType("Intelligent", {"iq": GraphQLField(GraphQLInt)})
-
-Human = GraphQLObjectType(
-    name="Human",
-    interfaces=[Being, Intelligent],
-    is_type_of=lambda: None,
-    fields={
-        "name": GraphQLField(
-            GraphQLString, {"surname": GraphQLArgument(GraphQLBoolean)}
-        ),
-        "pets": GraphQLField(GraphQLList(Pet)),
-        "iq": GraphQLField(GraphQLInt),
-    },
-)
-
-Alien = GraphQLObjectType(
-    name="Alien",
-    is_type_of=lambda *args: True,
-    interfaces=[Being, Intelligent],
-    fields={
-        "iq": GraphQLField(GraphQLInt),
-        "name": GraphQLField(
-            GraphQLString, {"surname": GraphQLArgument(GraphQLBoolean)}
-        ),
-        "numEyes": GraphQLField(GraphQLInt),
-    },
-)
-
-DogOrHuman = GraphQLUnionType("DogOrHuman", [Dog, Human])
-
-HumanOrAlien = GraphQLUnionType("HumanOrAlien", [Human, Alien])
-
-FurColor = GraphQLEnumType(
-    "FurColor",
-    {
-        "BROWN": GraphQLEnumValue(0),
-        "BLACK": GraphQLEnumValue(1),
-        "TAN": GraphQLEnumValue(2),
-        "SPOTTED": GraphQLEnumValue(3),
-    },
-)
-
-ComplexInput = GraphQLInputObjectType(
-    "ComplexInput",
-    {
-        "requiredField": GraphQLInputObjectField(GraphQLNonNull(GraphQLBoolean)),
-        "intField": GraphQLInputObjectField(GraphQLInt),
-        "stringField": GraphQLInputObjectField(GraphQLString),
-        "booleanField": GraphQLInputObjectField(GraphQLBoolean),
-        "stringListField": GraphQLInputObjectField(GraphQLList(GraphQLString)),
-    },
-)
-
-ComplicatedArgs = GraphQLObjectType(
-    "ComplicatedArgs",
-    {
-        "intArgField": GraphQLField(
-            GraphQLString, {"intArg": GraphQLArgument(GraphQLInt)}
-        ),
-        "nonNullIntArgField": GraphQLField(
-            GraphQLString,
-            {"nonNullIntArg": GraphQLArgument(GraphQLNonNull(GraphQLInt))},
-        ),
-        "stringArgField": GraphQLField(
-            GraphQLString, {"stringArg": GraphQLArgument(GraphQLString)}
-        ),
-        "booleanArgField": GraphQLField(
-            GraphQLString, {"booleanArg": GraphQLArgument(GraphQLBoolean)}
-        ),
-        "enumArgField": GraphQLField(
-            GraphQLString, {"enumArg": GraphQLArgument(FurColor)}
-        ),
-        "floatArgField": GraphQLField(
-            GraphQLString, {"floatArg": GraphQLArgument(GraphQLFloat)}
-        ),
-        "idArgField": GraphQLField(
-            GraphQLString, {"idArg": GraphQLArgument(GraphQLID)}
-        ),
-        "stringListArgField": GraphQLField(
-            GraphQLString,
-            {"stringListArg": GraphQLArgument(GraphQLList(GraphQLString))},
-        ),
-        "complexArgField": GraphQLField(
-            GraphQLString, {"complexArg": GraphQLArgument(ComplexInput)}
-        ),
-        "multipleReqs": GraphQLField(
-            GraphQLString,
-            {
-                "req1": GraphQLArgument(GraphQLNonNull(GraphQLInt)),
-                "req2": GraphQLArgument(GraphQLNonNull(GraphQLInt)),
-            },
-        ),
-        "multipleOpts": GraphQLField(
-            GraphQLString,
-            {
-                "opt1": GraphQLArgument(GraphQLInt, 0),
-                "opt2": GraphQLArgument(GraphQLInt, 0),
-            },
-        ),
-        "multipleOptsAndReq": GraphQLField(
-            GraphQLString,
-            {
-                "req1": GraphQLArgument(GraphQLNonNull(GraphQLInt)),
-                "req2": GraphQLArgument(GraphQLNonNull(GraphQLInt)),
-                "opt1": GraphQLArgument(GraphQLInt, 0),
-                "opt2": GraphQLArgument(GraphQLInt, 0),
-            },
-        ),
-    },
-)
-
-QueryRoot = GraphQLObjectType(
-    "QueryRoot",
-    {
-        "human": GraphQLField(Human, {"id": GraphQLArgument(GraphQLID)}),
-        "dog": GraphQLField(Dog),
-        "pet": GraphQLField(Pet),
-        "alien": GraphQLField(Alien),
-        "catOrDog": GraphQLField(CatOrDog),
-        "humanOrAlien": GraphQLField(HumanOrAlien),
-        "complicatedArgs": GraphQLField(ComplicatedArgs),
-    },
-)
-
-test_schema = GraphQLSchema(
-    query=QueryRoot,
-    directives=[
-        GraphQLIncludeDirective,
-        GraphQLSkipDirective,
-        GraphQLDirective(name="onQuery", locations=[DirectiveLocation.QUERY]),
-        GraphQLDirective(name="onMutation", locations=[DirectiveLocation.MUTATION]),
-        GraphQLDirective(
-            name="onSubscription", locations=[DirectiveLocation.SUBSCRIPTION]
-        ),
-        GraphQLDirective(name="onField", locations=[DirectiveLocation.FIELD]),
-        GraphQLDirective(
-            name="onFragmentDefinition",
-            locations=[DirectiveLocation.FRAGMENT_DEFINITION],
-        ),
-        GraphQLDirective(
-            name="onFragmentSpread", locations=[DirectiveLocation.FRAGMENT_SPREAD]
-        ),
-        GraphQLDirective(
-            name="onInlineFragment", locations=[DirectiveLocation.INLINE_FRAGMENT]
-        ),
-        GraphQLDirective(name="OnSchema", locations=[DirectiveLocation.SCHEMA]),
-        GraphQLDirective(name="onScalar", locations=[DirectiveLocation.SCALAR]),
-        GraphQLDirective(name="onObject", locations=[DirectiveLocation.OBJECT]),
-        GraphQLDirective(
-            name="onFieldDefinition", locations=[DirectiveLocation.FIELD_DEFINITION]
-        ),
-        GraphQLDirective(
-            name="onArgumentDefinition",
-            locations=[DirectiveLocation.ARGUMENT_DEFINITION],
-        ),
-        GraphQLDirective(name="onInterface", locations=[DirectiveLocation.INTERFACE]),
-        GraphQLDirective(name="onUnion", locations=[DirectiveLocation.UNION]),
-        GraphQLDirective(name="onEnum", locations=[DirectiveLocation.ENUM]),
-        GraphQLDirective(name="onEnumValue", locations=[DirectiveLocation.ENUM_VALUE]),
-        GraphQLDirective(
-            name="onInputObject", locations=[DirectiveLocation.INPUT_OBJECT]
-        ),
-        GraphQLDirective(
-            name="onInputFieldDefinition",
-            locations=[DirectiveLocation.INPUT_FIELD_DEFINITION],
-        ),
-    ],
-    types=[Cat, Dog, Human, Alien],
-)
-
-
-def expect_valid(schema, rules, query):
-    errors = validate(schema, parse(query), rules)
-    assert errors == [], "Error: %s, Should validate" % errors
-
-
-def sort_lists(value):
-    if isinstance(value, dict):
-        new_mapping = []
-        for k, v in value.items():
-            new_mapping.append((k, sort_lists(v)))
-        return sorted(new_mapping)
-    elif isinstance(value, list):
-        return sorted(map(sort_lists, value))
-    return value
-
-
-def expect_invalid(schema, rules, query, expected_errors, sort_list=True):
-    errors = validate(schema, parse(query), rules)
-    assert errors, "Should not validate"
-    for error in expected_errors:
-        error["locations"] = [
-            {"line": loc.line, "column": loc.column} for loc in error["locations"]
-        ]
-
-    if sort_list:
-        assert sort_lists(list(map(format_error, errors))) == sort_lists(
-            expected_errors
-        )
-
-    else:
-        assert list(map(format_error, errors)) == expected_errors
-
-
-def expect_passes_rule(rule, query):
-    return expect_valid(test_schema, [rule], query)
-
-
-def expect_fails_rule(rule, query, errors, sort_list=True):
-    return expect_invalid(test_schema, [rule], query, errors, sort_list)
-
-
-def expect_fails_rule_with_schema(schema, rule, query, errors, sort_list=True):
-    return expect_invalid(schema, [rule], query, errors, sort_list)
-
-
-def expect_passes_rule_with_schema(schema, rule, query):
-    return expect_valid(schema, [rule], query)
diff --git a/graphql/validation/validation.py b/graphql/validation/validation.py
deleted file mode 100644
index 1c3948f..0000000
--- a/graphql/validation/validation.py
+++ /dev/null
@@ -1,201 +0,0 @@
-from ..language.ast import FragmentDefinition, FragmentSpread, OperationDefinition
-from ..language.visitor import ParallelVisitor, TypeInfoVisitor, Visitor, visit
-from ..type import GraphQLSchema
-from ..utils.type_info import TypeInfo
-from .rules import specified_rules
-
-# Necessary for static type checking
-if False:  # flake8: noqa
-    from typing import List, Union, Optional, Dict, Set, Any, Type
-    from ..language.ast import Document, SelectionSet, Node
-    from ..error import GraphQLError
-    from .rules.base import ValidationRule
-    from ..type.definition import (
-        GraphQLObjectType,
-        GraphQLInterfaceType,
-        GraphQLField,
-        GraphQLArgument,
-        GraphQLType,
-        GraphQLInputObjectType,
-    )
-
-
-def validate(schema, ast, rules=specified_rules):
-    # type: (GraphQLSchema, Document, List[Type[ValidationRule]]) -> List
-    assert schema, "Must provide schema"
-    assert ast, "Must provide document"
-    assert isinstance(schema, GraphQLSchema)
-    type_info = TypeInfo(schema)
-    return visit_using_rules(schema, type_info, ast, rules)
-
-
-def visit_using_rules(schema, type_info, ast, rules):
-    # type: (GraphQLSchema, TypeInfo, Document, List[Type[ValidationRule]]) -> List
-    context = ValidationContext(schema, ast, type_info)
-    visitors = [rule(context) for rule in rules]
-    visit(ast, TypeInfoVisitor(type_info, ParallelVisitor(visitors)))
-    return context.get_errors()
-
-
-class VariableUsage(object):
-    __slots__ = "node", "type"
-
-    def __init__(self, node, type):
-        self.node = node
-        self.type = type
-
-
-class UsageVisitor(Visitor):
-    __slots__ = "usages", "type_info"
-
-    def __init__(self, usages, type_info):
-        # type: (List[VariableUsage], TypeInfo) -> None
-        self.usages = usages
-        self.type_info = type_info
-
-    def enter_VariableDefinition(self, node, key, parent, path, ancestors):
-        return False
-
-    def enter_Variable(self, node, key, parent, path, ancestors):
-        usage = VariableUsage(node, type=self.type_info.get_input_type())
-        self.usages.append(usage)
-
-
-class ValidationContext(object):
-    __slots__ = (
-        "_schema",
-        "_ast",
-        "_type_info",
-        "_errors",
-        "_fragments",
-        "_fragment_spreads",
-        "_recursively_referenced_fragments",
-        "_variable_usages",
-        "_recursive_variable_usages",
-    )
-
-    def __init__(self, schema, ast, type_info):
-        # type: (GraphQLSchema, Document, TypeInfo) -> None
-        self._schema = schema
-        self._ast = ast
-        self._type_info = type_info
-        self._errors = []  # type: List[GraphQLError]
-        self._fragments = None  # type: Optional[Dict[str, FragmentDefinition]]
-        self._fragment_spreads = {}  # type: Dict[Node, List[FragmentSpread]]
-        self._recursively_referenced_fragments = (
-            {}
-        )  # type: Dict[OperationDefinition, List[FragmentSpread]]
-        self._variable_usages = {}  # type: Dict[Node, List[VariableUsage]]
-        self._recursive_variable_usages = (
-            {}
-        )  # type: Dict[OperationDefinition, List[VariableUsage]]
-
-    def report_error(self, error):
-        self._errors.append(error)
-
-    def get_errors(self):
-        # type: () -> List
-        return self._errors
-
-    def get_schema(self):
-        # type: () -> GraphQLSchema
-        return self._schema
-
-    def get_variable_usages(self, node):
-        # type: (OperationDefinition) -> List[VariableUsage]
-        usages = self._variable_usages.get(node)
-        if usages is None:
-            usages = []
-            sub_visitor = UsageVisitor(usages, self._type_info)
-            visit(node, TypeInfoVisitor(self._type_info, sub_visitor))
-            self._variable_usages[node] = usages
-
-        return usages
-
-    def get_recursive_variable_usages(self, operation):
-        # type: (OperationDefinition) -> List[VariableUsage]
-        assert isinstance(operation, OperationDefinition)
-        usages = self._recursive_variable_usages.get(operation)
-        if usages is None:
-            usages = self.get_variable_usages(operation)
-            fragments = self.get_recursively_referenced_fragments(operation)
-            for fragment in fragments:
-                usages.extend(self.get_variable_usages(fragment))
-            self._recursive_variable_usages[operation] = usages
-
-        return usages
-
-    def get_recursively_referenced_fragments(self, operation):
-        # type: (OperationDefinition) -> List
-        assert isinstance(operation, OperationDefinition)
-        fragments = self._recursively_referenced_fragments.get(operation)
-        if not fragments:
-            fragments = []
-            collected_names = set()  # type: Set[str]
-            nodes_to_visit = [operation.selection_set]
-            while nodes_to_visit:
-                node = nodes_to_visit.pop()
-                spreads = self.get_fragment_spreads(node)
-                for spread in spreads:
-                    frag_name = spread.name.value
-                    if frag_name not in collected_names:
-                        collected_names.add(frag_name)
-                        fragment = self.get_fragment(frag_name)
-                        if fragment:
-                            fragments.append(fragment)
-                            nodes_to_visit.append(fragment.selection_set)
-            self._recursively_referenced_fragments[operation] = fragments
-        return fragments
-
-    def get_fragment_spreads(self, node):
-        # type: (SelectionSet) -> List[FragmentSpread]
-        spreads = self._fragment_spreads.get(node)
-        if not spreads:
-            spreads = []
-            sets_to_visit = [node]
-            while sets_to_visit:
-                _set = sets_to_visit.pop()
-                for selection in _set.selections:
-                    if isinstance(selection, FragmentSpread):
-                        spreads.append(selection)
-                    elif selection.selection_set:
-                        sets_to_visit.append(selection.selection_set)
-
-            self._fragment_spreads[node] = spreads
-        return spreads
-
-    def get_ast(self):
-        return self._ast
-
-    def get_fragment(self, name):
-        fragments = self._fragments
-        if fragments is None:
-            self._fragments = fragments = {}
-            for statement in self.get_ast().definitions:
-                if isinstance(statement, FragmentDefinition):
-                    fragments[statement.name.value] = statement
-        return fragments.get(name)
-
-    def get_type(self):
-        # type: () -> Optional[GraphQLType]
-        return self._type_info.get_type()
-
-    def get_parent_type(self):
-        # type: () -> Union[GraphQLInterfaceType, GraphQLObjectType, None]
-        return self._type_info.get_parent_type()
-
-    def get_input_type(self):
-        # type: () -> Optional[GraphQLInputObjectType]
-        return self._type_info.get_input_type()  # type: ignore
-
-    def get_field_def(self):
-        # type: () -> Optional[GraphQLField]
-        return self._type_info.get_field_def()
-
-    def get_directive(self):
-        # type: () -> Optional[Any]
-        return self._type_info.get_directive()
-
-    def get_argument(self):
-        # type: () -> Optional[GraphQLArgument]
-        return self._type_info.get_argument()
diff --git a/poetry.lock b/poetry.lock
new file mode 100644
index 0000000..69d2bd2
--- /dev/null
+++ b/poetry.lock
@@ -0,0 +1,1122 @@
+[[package]]
+category = "dev"
+description = "A configurable sidebar-enabled Sphinx theme"
+name = "alabaster"
+optional = false
+python-versions = "*"
+version = "0.7.12"
+
+[[package]]
+category = "dev"
+description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
+name = "appdirs"
+optional = false
+python-versions = "*"
+version = "1.4.4"
+
+[[package]]
+category = "dev"
+description = "Atomic file writes."
+marker = "sys_platform == \"win32\""
+name = "atomicwrites"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.4.0"
+
+[[package]]
+category = "dev"
+description = "Classes Without Boilerplate"
+name = "attrs"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "19.3.0"
+
+[package.extras]
+azure-pipelines = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "pytest-azurepipelines"]
+dev = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "sphinx", "pre-commit"]
+docs = ["sphinx", "zope.interface"]
+tests = ["coverage", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
+
+[[package]]
+category = "dev"
+description = "Internationalization utilities"
+name = "babel"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.8.0"
+
+[package.dependencies]
+pytz = ">=2015.7"
+
+[[package]]
+category = "dev"
+description = "The uncompromising code formatter."
+name = "black"
+optional = false
+python-versions = ">=3.6"
+version = "19.10b0"
+
+[package.dependencies]
+appdirs = "*"
+attrs = ">=18.1.0"
+click = ">=6.5"
+pathspec = ">=0.6,<1"
+regex = "*"
+toml = ">=0.9.4"
+typed-ast = ">=1.4.0"
+
+[package.extras]
+d = ["aiohttp (>=3.3.2)", "aiohttp-cors"]
+
+[[package]]
+category = "dev"
+description = "Version-bump your software with a single command!"
+name = "bump2version"
+optional = false
+python-versions = ">=3.5"
+version = "1.0.0"
+
+[[package]]
+category = "dev"
+description = "Python package for providing Mozilla's CA Bundle."
+name = "certifi"
+optional = false
+python-versions = "*"
+version = "2020.6.20"
+
+[[package]]
+category = "dev"
+description = "Universal encoding detector for Python 2 and 3"
+name = "chardet"
+optional = false
+python-versions = "*"
+version = "3.0.4"
+
+[[package]]
+category = "dev"
+description = "Check MANIFEST.in in a Python source package for completeness"
+name = "check-manifest"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "0.40"
+
+[package.dependencies]
+toml = "*"
+
+[package.extras]
+test = ["mock"]
+
+[[package]]
+category = "dev"
+description = "Composable command line interface toolkit"
+name = "click"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "7.1.2"
+
+[[package]]
+category = "dev"
+description = "Hosted coverage reports for GitHub, Bitbucket and Gitlab"
+name = "codecov"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.1.7"
+
+[package.dependencies]
+coverage = "*"
+requests = ">=2.7.9"
+
+[[package]]
+category = "dev"
+description = "Cross-platform colored terminal text."
+marker = "sys_platform == \"win32\" or platform_system == \"Windows\""
+name = "colorama"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "0.4.3"
+
+[[package]]
+category = "dev"
+description = "Code coverage measurement for Python"
+name = "coverage"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+version = "5.1"
+
+[package.extras]
+toml = ["toml"]
+
+[[package]]
+category = "dev"
+description = "Distribution utilities"
+name = "distlib"
+optional = false
+python-versions = "*"
+version = "0.3.1"
+
+[[package]]
+category = "dev"
+description = "Docutils -- Python Documentation Utilities"
+name = "docutils"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "0.16"
+
+[[package]]
+category = "dev"
+description = "A platform independent file lock."
+name = "filelock"
+optional = false
+python-versions = "*"
+version = "3.0.12"
+
+[[package]]
+category = "dev"
+description = "the modular source code checker: pep8 pyflakes and co"
+name = "flake8"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+version = "3.8.3"
+
+[package.dependencies]
+mccabe = ">=0.6.0,<0.7.0"
+pycodestyle = ">=2.6.0a1,<2.7.0"
+pyflakes = ">=2.2.0,<2.3.0"
+
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = "*"
+
+[[package]]
+category = "dev"
+description = "Internationalized Domain Names in Applications (IDNA)"
+name = "idna"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.10"
+
+[[package]]
+category = "dev"
+description = "Getting image size from png/jpeg/jpeg2000/gif file"
+name = "imagesize"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.2.0"
+
+[[package]]
+category = "dev"
+description = "Read metadata from Python packages"
+marker = "python_version < \"3.8\""
+name = "importlib-metadata"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+version = "1.7.0"
+
+[package.dependencies]
+zipp = ">=0.5"
+
+[package.extras]
+docs = ["sphinx", "rst.linker"]
+testing = ["packaging", "pep517", "importlib-resources (>=1.3)"]
+
+[[package]]
+category = "dev"
+description = "Read resources from Python packages"
+marker = "python_version < \"3.7\""
+name = "importlib-resources"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+version = "3.0.0"
+
+[package.dependencies]
+[package.dependencies.zipp]
+python = "<3.8"
+version = ">=0.4"
+
+[package.extras]
+docs = ["sphinx", "rst.linker", "jaraco.packaging"]
+
+[[package]]
+category = "dev"
+description = "A very fast and expressive template engine."
+name = "jinja2"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "2.11.2"
+
+[package.dependencies]
+MarkupSafe = ">=0.23"
+
+[package.extras]
+i18n = ["Babel (>=0.8)"]
+
+[[package]]
+category = "dev"
+description = "Safely add untrusted strings to HTML/XML markup."
+name = "markupsafe"
+optional = false
+python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
+version = "1.1.1"
+
+[[package]]
+category = "dev"
+description = "McCabe checker, plugin for flake8"
+name = "mccabe"
+optional = false
+python-versions = "*"
+version = "0.6.1"
+
+[[package]]
+category = "dev"
+description = "More routines for operating on iterables, beyond itertools"
+name = "more-itertools"
+optional = false
+python-versions = ">=3.5"
+version = "8.4.0"
+
+[[package]]
+category = "dev"
+description = "Optional static typing for Python"
+name = "mypy"
+optional = false
+python-versions = ">=3.5"
+version = "0.782"
+
+[package.dependencies]
+mypy-extensions = ">=0.4.3,<0.5.0"
+typed-ast = ">=1.4.0,<1.5.0"
+typing-extensions = ">=3.7.4"
+
+[package.extras]
+dmypy = ["psutil (>=4.0)"]
+
+[[package]]
+category = "dev"
+description = "Experimental type system extensions for programs checked with the mypy typechecker."
+name = "mypy-extensions"
+optional = false
+python-versions = "*"
+version = "0.4.3"
+
+[[package]]
+category = "dev"
+description = "Core utilities for Python packages"
+name = "packaging"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "20.4"
+
+[package.dependencies]
+pyparsing = ">=2.0.2"
+six = "*"
+
+[[package]]
+category = "dev"
+description = "Utility library for gitignore style pattern matching of file paths."
+name = "pathspec"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "0.8.0"
+
+[[package]]
+category = "dev"
+description = "plugin and hook calling mechanisms for python"
+name = "pluggy"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "0.13.1"
+
+[package.dependencies]
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = ">=0.12"
+
+[package.extras]
+dev = ["pre-commit", "tox"]
+
+[[package]]
+category = "dev"
+description = "library with cross-python path, ini-parsing, io, code, log facilities"
+name = "py"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "1.9.0"
+
+[[package]]
+category = "dev"
+description = "Get CPU info with pure Python 2 & 3"
+name = "py-cpuinfo"
+optional = false
+python-versions = "*"
+version = "6.0.0"
+
+[[package]]
+category = "dev"
+description = "Python style guide checker"
+name = "pycodestyle"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.6.0"
+
+[[package]]
+category = "dev"
+description = "passive checker of Python programs"
+name = "pyflakes"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
+version = "2.2.0"
+
+[[package]]
+category = "dev"
+description = "Pygments is a syntax highlighting package written in Python."
+name = "pygments"
+optional = false
+python-versions = ">=3.5"
+version = "2.6.1"
+
+[[package]]
+category = "dev"
+description = "Python parsing module"
+name = "pyparsing"
+optional = false
+python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
+version = "2.4.7"
+
+[[package]]
+category = "dev"
+description = "pytest: simple powerful testing with Python"
+name = "pytest"
+optional = false
+python-versions = ">=3.5"
+version = "5.4.3"
+
+[package.dependencies]
+atomicwrites = ">=1.0"
+attrs = ">=17.4.0"
+colorama = "*"
+more-itertools = ">=4.0.0"
+packaging = "*"
+pluggy = ">=0.12,<1.0"
+py = ">=1.5.0"
+wcwidth = "*"
+
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = ">=0.12"
+
+[package.extras]
+checkqa-mypy = ["mypy (v0.761)"]
+testing = ["argcomplete", "hypothesis (>=3.56)", "mock", "nose", "requests", "xmlschema"]
+
+[[package]]
+category = "dev"
+description = "Pytest support for asyncio."
+name = "pytest-asyncio"
+optional = false
+python-versions = ">= 3.5"
+version = "0.14.0"
+
+[package.dependencies]
+pytest = ">=5.4.0"
+
+[package.extras]
+testing = ["async-generator (>=1.3)", "coverage", "hypothesis (>=5.7.1)"]
+
+[[package]]
+category = "dev"
+description = "A ``pytest`` fixture for benchmarking code. It will group the tests into rounds that are calibrated to the chosen timer. See calibration_ and FAQ_."
+name = "pytest-benchmark"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "3.2.3"
+
+[package.dependencies]
+py-cpuinfo = "*"
+pytest = ">=3.8"
+
+[package.extras]
+aspect = ["aspectlib"]
+elasticsearch = ["elasticsearch"]
+histogram = ["pygal", "pygaljs"]
+
+[[package]]
+category = "dev"
+description = "Pytest plugin for measuring coverage."
+name = "pytest-cov"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "2.10.0"
+
+[package.dependencies]
+coverage = ">=4.4"
+pytest = ">=4.6"
+
+[package.extras]
+testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"]
+
+[[package]]
+category = "dev"
+description = "Describe-style plugin for pytest"
+name = "pytest-describe"
+optional = false
+python-versions = "*"
+version = "1.0.0"
+
+[package.dependencies]
+pytest = ">=2.6.0"
+
+[[package]]
+category = "dev"
+description = "py.test plugin to abort hanging tests"
+name = "pytest-timeout"
+optional = false
+python-versions = "*"
+version = "1.4.1"
+
+[package.dependencies]
+pytest = ">=3.6.0"
+
+[[package]]
+category = "dev"
+description = "World timezone definitions, modern and historical"
+name = "pytz"
+optional = false
+python-versions = "*"
+version = "2020.1"
+
+[[package]]
+category = "dev"
+description = "Alternative regular expression module, to replace re."
+name = "regex"
+optional = false
+python-versions = "*"
+version = "2020.6.8"
+
+[[package]]
+category = "dev"
+description = "Python HTTP for Humans."
+name = "requests"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "2.24.0"
+
+[package.dependencies]
+certifi = ">=2017.4.17"
+chardet = ">=3.0.2,<4"
+idna = ">=2.5,<3"
+urllib3 = ">=1.21.1,<1.25.0 || >1.25.0,<1.25.1 || >1.25.1,<1.26"
+
+[package.extras]
+security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
+socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)", "win-inet-pton"]
+
+[[package]]
+category = "dev"
+description = "Python 2 and 3 compatibility utilities"
+name = "six"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
+version = "1.15.0"
+
+[[package]]
+category = "dev"
+description = "This package provides 26 stemmers for 25 languages generated from Snowball algorithms."
+name = "snowballstemmer"
+optional = false
+python-versions = "*"
+version = "2.0.0"
+
+[[package]]
+category = "dev"
+description = "Python documentation generator"
+name = "sphinx"
+optional = false
+python-versions = ">=3.5"
+version = "2.4.4"
+
+[package.dependencies]
+Jinja2 = ">=2.3"
+Pygments = ">=2.0"
+alabaster = ">=0.7,<0.8"
+babel = ">=1.3,<2.0 || >2.0"
+colorama = ">=0.3.5"
+docutils = ">=0.12"
+imagesize = "*"
+packaging = "*"
+requests = ">=2.5.0"
+setuptools = "*"
+snowballstemmer = ">=1.1"
+sphinxcontrib-applehelp = "*"
+sphinxcontrib-devhelp = "*"
+sphinxcontrib-htmlhelp = "*"
+sphinxcontrib-jsmath = "*"
+sphinxcontrib-qthelp = "*"
+sphinxcontrib-serializinghtml = "*"
+
+[package.extras]
+docs = ["sphinxcontrib-websupport"]
+test = ["pytest (<5.3.3)", "pytest-cov", "html5lib", "flake8 (>=3.5.0)", "flake8-import-order", "mypy (>=0.761)", "docutils-stubs"]
+
+[[package]]
+category = "dev"
+description = "Read the Docs theme for Sphinx"
+name = "sphinx-rtd-theme"
+optional = false
+python-versions = "*"
+version = "0.5.0"
+
+[package.dependencies]
+sphinx = "*"
+
+[package.extras]
+dev = ["transifex-client", "sphinxcontrib-httpdomain", "bump2version"]
+
+[[package]]
+category = "dev"
+description = "sphinxcontrib-applehelp is a sphinx extension which outputs Apple help books"
+name = "sphinxcontrib-applehelp"
+optional = false
+python-versions = ">=3.5"
+version = "1.0.2"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest"]
+
+[[package]]
+category = "dev"
+description = "sphinxcontrib-devhelp is a sphinx extension which outputs Devhelp document."
+name = "sphinxcontrib-devhelp"
+optional = false
+python-versions = ">=3.5"
+version = "1.0.2"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest"]
+
+[[package]]
+category = "dev"
+description = "sphinxcontrib-htmlhelp is a sphinx extension which renders HTML help files"
+name = "sphinxcontrib-htmlhelp"
+optional = false
+python-versions = ">=3.5"
+version = "1.0.3"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest", "html5lib"]
+
+[[package]]
+category = "dev"
+description = "A sphinx extension which renders display math in HTML via JavaScript"
+name = "sphinxcontrib-jsmath"
+optional = false
+python-versions = ">=3.5"
+version = "1.0.1"
+
+[package.extras]
+test = ["pytest", "flake8", "mypy"]
+
+[[package]]
+category = "dev"
+description = "sphinxcontrib-qthelp is a sphinx extension which outputs QtHelp document."
+name = "sphinxcontrib-qthelp"
+optional = false
+python-versions = ">=3.5"
+version = "1.0.3"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest"]
+
+[[package]]
+category = "dev"
+description = "sphinxcontrib-serializinghtml is a sphinx extension which outputs \"serialized\" HTML files (json and pickle)."
+name = "sphinxcontrib-serializinghtml"
+optional = false
+python-versions = ">=3.5"
+version = "1.1.4"
+
+[package.extras]
+lint = ["flake8", "mypy", "docutils-stubs"]
+test = ["pytest"]
+
+[[package]]
+category = "dev"
+description = "Python Library for Tom's Obvious, Minimal Language"
+name = "toml"
+optional = false
+python-versions = "*"
+version = "0.10.1"
+
+[[package]]
+category = "dev"
+description = "tox is a generic virtualenv management and test command line tool"
+name = "tox"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
+version = "3.16.1"
+
+[package.dependencies]
+colorama = ">=0.4.1"
+filelock = ">=3.0.0"
+packaging = ">=14"
+pluggy = ">=0.12.0"
+py = ">=1.4.17"
+six = ">=1.14.0"
+toml = ">=0.9.4"
+virtualenv = ">=16.0.0,<20.0.0 || >20.0.0,<20.0.1 || >20.0.1,<20.0.2 || >20.0.2,<20.0.3 || >20.0.3,<20.0.4 || >20.0.4,<20.0.5 || >20.0.5,<20.0.6 || >20.0.6,<20.0.7 || >20.0.7"
+
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = ">=0.12,<2"
+
+[package.extras]
+docs = ["sphinx (>=2.0.0)", "towncrier (>=18.5.0)", "pygments-github-lexers (>=0.0.5)", "sphinxcontrib-autoprogram (>=0.1.5)"]
+testing = ["freezegun (>=0.3.11)", "pathlib2 (>=2.3.3)", "pytest (>=4.0.0)", "pytest-cov (>=2.5.1)", "pytest-mock (>=1.10.0)", "pytest-xdist (>=1.22.2)", "pytest-randomly (>=1.0.0)", "flaky (>=3.4.0)", "psutil (>=5.6.1)"]
+
+[[package]]
+category = "dev"
+description = "a fork of Python 2 and 3 ast modules with type comment support"
+name = "typed-ast"
+optional = false
+python-versions = "*"
+version = "1.4.1"
+
+[[package]]
+category = "dev"
+description = "Backported and Experimental Type Hints for Python 3.5+"
+name = "typing-extensions"
+optional = false
+python-versions = "*"
+version = "3.7.4.2"
+
+[[package]]
+category = "dev"
+description = "HTTP library with thread-safe connection pooling, file post, and more."
+name = "urllib3"
+optional = false
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
+version = "1.25.9"
+
+[package.extras]
+brotli = ["brotlipy (>=0.6.0)"]
+secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "pyOpenSSL (>=0.14)", "ipaddress"]
+socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"]
+
+[[package]]
+category = "dev"
+description = "Virtual Python Environment builder"
+name = "virtualenv"
+optional = false
+python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
+version = "20.0.25"
+
+[package.dependencies]
+appdirs = ">=1.4.3,<2"
+distlib = ">=0.3.0,<1"
+filelock = ">=3.0.0,<4"
+six = ">=1.9.0,<2"
+
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = ">=0.12,<2"
+
+[package.dependencies.importlib-resources]
+python = "<3.7"
+version = ">=1.0"
+
+[package.extras]
+docs = ["sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=19.9.0rc1)", "proselint (>=0.10.2)"]
+testing = ["pytest (>=4)", "coverage (>=5)", "coverage-enable-subprocess (>=1)", "pytest-xdist (>=1.31.0)", "pytest-mock (>=2)", "pytest-env (>=0.6.2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "pytest-freezegun (>=0.4.1)", "flaky (>=3)", "packaging (>=20.0)", "xonsh (>=0.9.16)"]
+
+[[package]]
+category = "dev"
+description = "Measures the displayed width of unicode strings in a terminal"
+name = "wcwidth"
+optional = false
+python-versions = "*"
+version = "0.2.5"
+
+[[package]]
+category = "dev"
+description = "Backport of pathlib-compatible object wrapper for zip files"
+marker = "python_version < \"3.8\""
+name = "zipp"
+optional = false
+python-versions = ">=3.6"
+version = "3.1.0"
+
+[package.extras]
+docs = ["sphinx", "jaraco.packaging (>=3.2)", "rst.linker (>=1.9)"]
+testing = ["jaraco.itertools", "func-timeout"]
+
+[metadata]
+content-hash = "e39116c151dc49dcea69d5e32850bb695985fb46db2ba200321f56a43abf26b1"
+python-versions = "^3.6"
+
+[metadata.files]
+alabaster = [
+    {file = "alabaster-0.7.12-py2.py3-none-any.whl", hash = "sha256:446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359"},
+    {file = "alabaster-0.7.12.tar.gz", hash = "sha256:a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"},
+]
+appdirs = [
+    {file = "appdirs-1.4.4-py2.py3-none-any.whl", hash = "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"},
+    {file = "appdirs-1.4.4.tar.gz", hash = "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41"},
+]
+atomicwrites = [
+    {file = "atomicwrites-1.4.0-py2.py3-none-any.whl", hash = "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197"},
+    {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
+]
+attrs = [
+    {file = "attrs-19.3.0-py2.py3-none-any.whl", hash = "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c"},
+    {file = "attrs-19.3.0.tar.gz", hash = "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"},
+]
+babel = [
+    {file = "Babel-2.8.0-py2.py3-none-any.whl", hash = "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4"},
+    {file = "Babel-2.8.0.tar.gz", hash = "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38"},
+]
+black = [
+    {file = "black-19.10b0-py36-none-any.whl", hash = "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b"},
+    {file = "black-19.10b0.tar.gz", hash = "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"},
+]
+bump2version = [
+    {file = "bump2version-1.0.0-py2.py3-none-any.whl", hash = "sha256:477f0e18a0d58e50bb3dbc9af7fcda464fd0ebfc7a6151d8888602d7153171a0"},
+    {file = "bump2version-1.0.0.tar.gz", hash = "sha256:cd4f3a231305e405ed8944d8ff35bd742d9bc740ad62f483bd0ca21ce7131984"},
+]
+certifi = [
+    {file = "certifi-2020.6.20-py2.py3-none-any.whl", hash = "sha256:8fc0819f1f30ba15bdb34cceffb9ef04d99f420f68eb75d901e9560b8749fc41"},
+    {file = "certifi-2020.6.20.tar.gz", hash = "sha256:5930595817496dd21bb8dc35dad090f1c2cd0adfaf21204bf6732ca5d8ee34d3"},
+]
+chardet = [
+    {file = "chardet-3.0.4-py2.py3-none-any.whl", hash = "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"},
+    {file = "chardet-3.0.4.tar.gz", hash = "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae"},
+]
+check-manifest = [
+    {file = "check-manifest-0.40.tar.gz", hash = "sha256:42de6eaab4ed149e60c9b367ada54f01a3b1e4d6846784f9b9710e770ff5572c"},
+    {file = "check_manifest-0.40-py2.py3-none-any.whl", hash = "sha256:78dd077f2c70dbac7cfcc9d12cbd423914e787ea4b5631de45aecd25b524e8e3"},
+]
+click = [
+    {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
+    {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
+]
+codecov = [
+    {file = "codecov-2.1.7-py2.py3-none-any.whl", hash = "sha256:b67bb8029e8340a7bf22c71cbece5bd18c96261fdebc2f105ee4d5a005bc8728"},
+    {file = "codecov-2.1.7-py3.8.egg", hash = "sha256:d8b8109f44edad03b24f5f189dac8de9b1e3dc3c791fa37eeaf8c7381503ec34"},
+    {file = "codecov-2.1.7.tar.gz", hash = "sha256:491938ad774ea94a963d5d16354c7299e90422a33a353ba0d38d0943ed1d5091"},
+]
+colorama = [
+    {file = "colorama-0.4.3-py2.py3-none-any.whl", hash = "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff"},
+    {file = "colorama-0.4.3.tar.gz", hash = "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"},
+]
+coverage = [
+    {file = "coverage-5.1-cp27-cp27m-macosx_10_12_x86_64.whl", hash = "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65"},
+    {file = "coverage-5.1-cp27-cp27m-macosx_10_13_intel.whl", hash = "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2"},
+    {file = "coverage-5.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04"},
+    {file = "coverage-5.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6"},
+    {file = "coverage-5.1-cp27-cp27m-win32.whl", hash = "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796"},
+    {file = "coverage-5.1-cp27-cp27m-win_amd64.whl", hash = "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730"},
+    {file = "coverage-5.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0"},
+    {file = "coverage-5.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a"},
+    {file = "coverage-5.1-cp35-cp35m-macosx_10_12_x86_64.whl", hash = "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf"},
+    {file = "coverage-5.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9"},
+    {file = "coverage-5.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768"},
+    {file = "coverage-5.1-cp35-cp35m-win32.whl", hash = "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2"},
+    {file = "coverage-5.1-cp35-cp35m-win_amd64.whl", hash = "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7"},
+    {file = "coverage-5.1-cp36-cp36m-macosx_10_13_x86_64.whl", hash = "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0"},
+    {file = "coverage-5.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019"},
+    {file = "coverage-5.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c"},
+    {file = "coverage-5.1-cp36-cp36m-win32.whl", hash = "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1"},
+    {file = "coverage-5.1-cp36-cp36m-win_amd64.whl", hash = "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7"},
+    {file = "coverage-5.1-cp37-cp37m-macosx_10_13_x86_64.whl", hash = "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355"},
+    {file = "coverage-5.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489"},
+    {file = "coverage-5.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd"},
+    {file = "coverage-5.1-cp37-cp37m-win32.whl", hash = "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e"},
+    {file = "coverage-5.1-cp37-cp37m-win_amd64.whl", hash = "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a"},
+    {file = "coverage-5.1-cp38-cp38-macosx_10_13_x86_64.whl", hash = "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55"},
+    {file = "coverage-5.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c"},
+    {file = "coverage-5.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef"},
+    {file = "coverage-5.1-cp38-cp38-win32.whl", hash = "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24"},
+    {file = "coverage-5.1-cp38-cp38-win_amd64.whl", hash = "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0"},
+    {file = "coverage-5.1-cp39-cp39-win32.whl", hash = "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4"},
+    {file = "coverage-5.1-cp39-cp39-win_amd64.whl", hash = "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e"},
+    {file = "coverage-5.1.tar.gz", hash = "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"},
+]
+distlib = [
+    {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
+    {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
+]
+docutils = [
+    {file = "docutils-0.16-py2.py3-none-any.whl", hash = "sha256:0c5b78adfbf7762415433f5515cd5c9e762339e23369dbe8000d84a4bf4ab3af"},
+    {file = "docutils-0.16.tar.gz", hash = "sha256:c2de3a60e9e7d07be26b7f2b00ca0309c207e06c100f9cc2a94931fc75a478fc"},
+]
+filelock = [
+    {file = "filelock-3.0.12-py3-none-any.whl", hash = "sha256:929b7d63ec5b7d6b71b0fa5ac14e030b3f70b75747cef1b10da9b879fef15836"},
+    {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
+]
+flake8 = [
+    {file = "flake8-3.8.3-py2.py3-none-any.whl", hash = "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c"},
+    {file = "flake8-3.8.3.tar.gz", hash = "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"},
+]
+idna = [
+    {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
+    {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
+]
+imagesize = [
+    {file = "imagesize-1.2.0-py2.py3-none-any.whl", hash = "sha256:6965f19a6a2039c7d48bca7dba2473069ff854c36ae6f19d2cde309d998228a1"},
+    {file = "imagesize-1.2.0.tar.gz", hash = "sha256:b1f6b5a4eab1f73479a50fb79fcf729514a900c341d8503d62a62dbc4127a2b1"},
+]
+importlib-metadata = [
+    {file = "importlib_metadata-1.7.0-py2.py3-none-any.whl", hash = "sha256:dc15b2969b4ce36305c51eebe62d418ac7791e9a157911d58bfb1f9ccd8e2070"},
+    {file = "importlib_metadata-1.7.0.tar.gz", hash = "sha256:90bb658cdbbf6d1735b6341ce708fc7024a3e14e99ffdc5783edea9f9b077f83"},
+]
+importlib-resources = [
+    {file = "importlib_resources-3.0.0-py2.py3-none-any.whl", hash = "sha256:d028f66b66c0d5732dae86ba4276999855e162a749c92620a38c1d779ed138a7"},
+    {file = "importlib_resources-3.0.0.tar.gz", hash = "sha256:19f745a6eca188b490b1428c8d1d4a0d2368759f32370ea8fb89cad2ab1106c3"},
+]
+jinja2 = [
+    {file = "Jinja2-2.11.2-py2.py3-none-any.whl", hash = "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"},
+    {file = "Jinja2-2.11.2.tar.gz", hash = "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0"},
+]
+markupsafe = [
+    {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
+    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
+    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"},
+    {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"},
+    {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"},
+    {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"},
+    {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"},
+    {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"},
+    {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"},
+    {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"},
+    {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"},
+    {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"},
+    {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"},
+    {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"},
+    {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"},
+    {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
+    {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
+    {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
+    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
+    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
+    {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
+    {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
+    {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
+    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
+    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
+    {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
+    {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
+    {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
+]
+mccabe = [
+    {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
+    {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
+]
+more-itertools = [
+    {file = "more-itertools-8.4.0.tar.gz", hash = "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5"},
+    {file = "more_itertools-8.4.0-py3-none-any.whl", hash = "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"},
+]
+mypy = [
+    {file = "mypy-0.782-cp35-cp35m-macosx_10_6_x86_64.whl", hash = "sha256:2c6cde8aa3426c1682d35190b59b71f661237d74b053822ea3d748e2c9578a7c"},
+    {file = "mypy-0.782-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:9c7a9a7ceb2871ba4bac1cf7217a7dd9ccd44c27c2950edbc6dc08530f32ad4e"},
+    {file = "mypy-0.782-cp35-cp35m-win_amd64.whl", hash = "sha256:c05b9e4fb1d8a41d41dec8786c94f3b95d3c5f528298d769eb8e73d293abc48d"},
+    {file = "mypy-0.782-cp36-cp36m-macosx_10_6_x86_64.whl", hash = "sha256:6731603dfe0ce4352c555c6284c6db0dc935b685e9ce2e4cf220abe1e14386fd"},
+    {file = "mypy-0.782-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:f05644db6779387ccdb468cc47a44b4356fc2ffa9287135d05b70a98dc83b89a"},
+    {file = "mypy-0.782-cp36-cp36m-win_amd64.whl", hash = "sha256:b7fbfabdbcc78c4f6fc4712544b9b0d6bf171069c6e0e3cb82440dd10ced3406"},
+    {file = "mypy-0.782-cp37-cp37m-macosx_10_6_x86_64.whl", hash = "sha256:3fdda71c067d3ddfb21da4b80e2686b71e9e5c72cca65fa216d207a358827f86"},
+    {file = "mypy-0.782-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:d7df6eddb6054d21ca4d3c6249cae5578cb4602951fd2b6ee2f5510ffb098707"},
+    {file = "mypy-0.782-cp37-cp37m-win_amd64.whl", hash = "sha256:a4a2cbcfc4cbf45cd126f531dedda8485671545b43107ded25ce952aac6fb308"},
+    {file = "mypy-0.782-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6bb93479caa6619d21d6e7160c552c1193f6952f0668cdda2f851156e85186fc"},
+    {file = "mypy-0.782-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:81c7908b94239c4010e16642c9102bfc958ab14e36048fa77d0be3289dda76ea"},
+    {file = "mypy-0.782-cp38-cp38-win_amd64.whl", hash = "sha256:5dd13ff1f2a97f94540fd37a49e5d255950ebcdf446fb597463a40d0df3fac8b"},
+    {file = "mypy-0.782-py3-none-any.whl", hash = "sha256:e0b61738ab504e656d1fe4ff0c0601387a5489ca122d55390ade31f9ca0e252d"},
+    {file = "mypy-0.782.tar.gz", hash = "sha256:eff7d4a85e9eea55afa34888dfeaccde99e7520b51f867ac28a48492c0b1130c"},
+]
+mypy-extensions = [
+    {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
+    {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
+]
+packaging = [
+    {file = "packaging-20.4-py2.py3-none-any.whl", hash = "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"},
+    {file = "packaging-20.4.tar.gz", hash = "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8"},
+]
+pathspec = [
+    {file = "pathspec-0.8.0-py2.py3-none-any.whl", hash = "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0"},
+    {file = "pathspec-0.8.0.tar.gz", hash = "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"},
+]
+pluggy = [
+    {file = "pluggy-0.13.1-py2.py3-none-any.whl", hash = "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"},
+    {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
+]
+py = [
+    {file = "py-1.9.0-py2.py3-none-any.whl", hash = "sha256:366389d1db726cd2fcfc79732e75410e5fe4d31db13692115529d34069a043c2"},
+    {file = "py-1.9.0.tar.gz", hash = "sha256:9ca6883ce56b4e8da7e79ac18787889fa5206c79dcc67fb065376cd2fe03f342"},
+]
+py-cpuinfo = [
+    {file = "py-cpuinfo-6.0.0.tar.gz", hash = "sha256:7ffb31dea845b9f359b99bd5f7eea72dc70f852e0e34547d261a630f2b8c9c61"},
+]
+pycodestyle = [
+    {file = "pycodestyle-2.6.0-py2.py3-none-any.whl", hash = "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367"},
+    {file = "pycodestyle-2.6.0.tar.gz", hash = "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"},
+]
+pyflakes = [
+    {file = "pyflakes-2.2.0-py2.py3-none-any.whl", hash = "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92"},
+    {file = "pyflakes-2.2.0.tar.gz", hash = "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"},
+]
+pygments = [
+    {file = "Pygments-2.6.1-py3-none-any.whl", hash = "sha256:ff7a40b4860b727ab48fad6360eb351cc1b33cbf9b15a0f689ca5353e9463324"},
+    {file = "Pygments-2.6.1.tar.gz", hash = "sha256:647344a061c249a3b74e230c739f434d7ea4d8b1d5f3721bc0f3558049b38f44"},
+]
+pyparsing = [
+    {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
+    {file = "pyparsing-2.4.7.tar.gz", hash = "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1"},
+]
+pytest = [
+    {file = "pytest-5.4.3-py3-none-any.whl", hash = "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1"},
+    {file = "pytest-5.4.3.tar.gz", hash = "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"},
+]
+pytest-asyncio = [
+    {file = "pytest-asyncio-0.14.0.tar.gz", hash = "sha256:9882c0c6b24429449f5f969a5158b528f39bde47dc32e85b9f0403965017e700"},
+    {file = "pytest_asyncio-0.14.0-py3-none-any.whl", hash = "sha256:2eae1e34f6c68fc0a9dc12d4bea190483843ff4708d24277c41568d6b6044f1d"},
+]
+pytest-benchmark = [
+    {file = "pytest-benchmark-3.2.3.tar.gz", hash = "sha256:ad4314d093a3089701b24c80a05121994c7765ce373478c8f4ba8d23c9ba9528"},
+    {file = "pytest_benchmark-3.2.3-py2.py3-none-any.whl", hash = "sha256:01f79d38d506f5a3a0a9ada22ded714537bbdfc8147a881a35c1655db07289d9"},
+]
+pytest-cov = [
+    {file = "pytest-cov-2.10.0.tar.gz", hash = "sha256:1a629dc9f48e53512fcbfda6b07de490c374b0c83c55ff7a1720b3fccff0ac87"},
+    {file = "pytest_cov-2.10.0-py2.py3-none-any.whl", hash = "sha256:6e6d18092dce6fad667cd7020deed816f858ad3b49d5b5e2b1cc1c97a4dba65c"},
+]
+pytest-describe = [
+    {file = "pytest-describe-1.0.0.tar.gz", hash = "sha256:3e2ea0e77efa09edb98cf90423bf1da21a462ed90bd3120f8f98fe7519a167d5"},
+    {file = "pytest_describe-1.0.0-py2-none-any.whl", hash = "sha256:cc3862662faa5a6fb721927aaef46b46cf787e4a8163e5459fc8778e650fabad"},
+    {file = "pytest_describe-1.0.0-py3-none-any.whl", hash = "sha256:95fe78639d4d16c4a1e7d62c70f63030b217c08d2ee6dca49559fe6e730c6696"},
+]
+pytest-timeout = [
+    {file = "pytest-timeout-1.4.1.tar.gz", hash = "sha256:6d0fb4ce74cebb81be252e4e0d9c2a91f30270b33208cfa0f1da6eed9abf18ac"},
+    {file = "pytest_timeout-1.4.1-py2.py3-none-any.whl", hash = "sha256:c10650550e0c4fef5b06274411377c8b54c7b370c34b632fd4ce1a9b170f5ba3"},
+]
+pytz = [
+    {file = "pytz-2020.1-py2.py3-none-any.whl", hash = "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed"},
+    {file = "pytz-2020.1.tar.gz", hash = "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"},
+]
+regex = [
+    {file = "regex-2020.6.8-cp27-cp27m-win32.whl", hash = "sha256:fbff901c54c22425a5b809b914a3bfaf4b9570eee0e5ce8186ac71eb2025191c"},
+    {file = "regex-2020.6.8-cp27-cp27m-win_amd64.whl", hash = "sha256:112e34adf95e45158c597feea65d06a8124898bdeac975c9087fe71b572bd938"},
+    {file = "regex-2020.6.8-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:92d8a043a4241a710c1cf7593f5577fbb832cf6c3a00ff3fc1ff2052aff5dd89"},
+    {file = "regex-2020.6.8-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:bae83f2a56ab30d5353b47f9b2a33e4aac4de9401fb582b55c42b132a8ac3868"},
+    {file = "regex-2020.6.8-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:b2ba0f78b3ef375114856cbdaa30559914d081c416b431f2437f83ce4f8b7f2f"},
+    {file = "regex-2020.6.8-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:95fa7726d073c87141f7bbfb04c284901f8328e2d430eeb71b8ffdd5742a5ded"},
+    {file = "regex-2020.6.8-cp36-cp36m-win32.whl", hash = "sha256:e3cdc9423808f7e1bb9c2e0bdb1c9dc37b0607b30d646ff6faf0d4e41ee8fee3"},
+    {file = "regex-2020.6.8-cp36-cp36m-win_amd64.whl", hash = "sha256:c78e66a922de1c95a208e4ec02e2e5cf0bb83a36ceececc10a72841e53fbf2bd"},
+    {file = "regex-2020.6.8-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:08997a37b221a3e27d68ffb601e45abfb0093d39ee770e4257bd2f5115e8cb0a"},
+    {file = "regex-2020.6.8-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:2f6f211633ee8d3f7706953e9d3edc7ce63a1d6aad0be5dcee1ece127eea13ae"},
+    {file = "regex-2020.6.8-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:55b4c25cbb3b29f8d5e63aeed27b49fa0f8476b0d4e1b3171d85db891938cc3a"},
+    {file = "regex-2020.6.8-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:89cda1a5d3e33ec9e231ece7307afc101b5217523d55ef4dc7fb2abd6de71ba3"},
+    {file = "regex-2020.6.8-cp37-cp37m-win32.whl", hash = "sha256:690f858d9a94d903cf5cada62ce069b5d93b313d7d05456dbcd99420856562d9"},
+    {file = "regex-2020.6.8-cp37-cp37m-win_amd64.whl", hash = "sha256:1700419d8a18c26ff396b3b06ace315b5f2a6e780dad387e4c48717a12a22c29"},
+    {file = "regex-2020.6.8-cp38-cp38-manylinux1_i686.whl", hash = "sha256:654cb773b2792e50151f0e22be0f2b6e1c3a04c5328ff1d9d59c0398d37ef610"},
+    {file = "regex-2020.6.8-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:52e1b4bef02f4040b2fd547357a170fc1146e60ab310cdbdd098db86e929b387"},
+    {file = "regex-2020.6.8-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:cf59bbf282b627130f5ba68b7fa3abdb96372b24b66bdf72a4920e8153fc7910"},
+    {file = "regex-2020.6.8-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:5aaa5928b039ae440d775acea11d01e42ff26e1561c0ffcd3d805750973c6baf"},
+    {file = "regex-2020.6.8-cp38-cp38-win32.whl", hash = "sha256:97712e0d0af05febd8ab63d2ef0ab2d0cd9deddf4476f7aa153f76feef4b2754"},
+    {file = "regex-2020.6.8-cp38-cp38-win_amd64.whl", hash = "sha256:6ad8663c17db4c5ef438141f99e291c4d4edfeaacc0ce28b5bba2b0bf273d9b5"},
+    {file = "regex-2020.6.8.tar.gz", hash = "sha256:e9b64e609d37438f7d6e68c2546d2cb8062f3adb27e6336bc129b51be20773ac"},
+]
+requests = [
+    {file = "requests-2.24.0-py2.py3-none-any.whl", hash = "sha256:fe75cc94a9443b9246fc7049224f75604b113c36acb93f87b80ed42c44cbb898"},
+    {file = "requests-2.24.0.tar.gz", hash = "sha256:b3559a131db72c33ee969480840fff4bb6dd111de7dd27c8ee1f820f4f00231b"},
+]
+six = [
+    {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
+    {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
+]
+snowballstemmer = [
+    {file = "snowballstemmer-2.0.0-py2.py3-none-any.whl", hash = "sha256:209f257d7533fdb3cb73bdbd24f436239ca3b2fa67d56f6ff88e86be08cc5ef0"},
+    {file = "snowballstemmer-2.0.0.tar.gz", hash = "sha256:df3bac3df4c2c01363f3dd2cfa78cce2840a79b9f1c2d2de9ce8d31683992f52"},
+]
+sphinx = [
+    {file = "Sphinx-2.4.4-py3-none-any.whl", hash = "sha256:fc312670b56cb54920d6cc2ced455a22a547910de10b3142276495ced49231cb"},
+    {file = "Sphinx-2.4.4.tar.gz", hash = "sha256:b4c750d546ab6d7e05bdff6ac24db8ae3e8b8253a3569b754e445110a0a12b66"},
+]
+sphinx-rtd-theme = [
+    {file = "sphinx_rtd_theme-0.5.0-py2.py3-none-any.whl", hash = "sha256:373413d0f82425aaa28fb288009bf0d0964711d347763af2f1b65cafcb028c82"},
+    {file = "sphinx_rtd_theme-0.5.0.tar.gz", hash = "sha256:22c795ba2832a169ca301cd0a083f7a434e09c538c70beb42782c073651b707d"},
+]
+sphinxcontrib-applehelp = [
+    {file = "sphinxcontrib-applehelp-1.0.2.tar.gz", hash = "sha256:a072735ec80e7675e3f432fcae8610ecf509c5f1869d17e2eecff44389cdbc58"},
+    {file = "sphinxcontrib_applehelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:806111e5e962be97c29ec4c1e7fe277bfd19e9652fb1a4392105b43e01af885a"},
+]
+sphinxcontrib-devhelp = [
+    {file = "sphinxcontrib-devhelp-1.0.2.tar.gz", hash = "sha256:ff7f1afa7b9642e7060379360a67e9c41e8f3121f2ce9164266f61b9f4b338e4"},
+    {file = "sphinxcontrib_devhelp-1.0.2-py2.py3-none-any.whl", hash = "sha256:8165223f9a335cc1af7ffe1ed31d2871f325254c0423bc0c4c7cd1c1e4734a2e"},
+]
+sphinxcontrib-htmlhelp = [
+    {file = "sphinxcontrib-htmlhelp-1.0.3.tar.gz", hash = "sha256:e8f5bb7e31b2dbb25b9cc435c8ab7a79787ebf7f906155729338f3156d93659b"},
+    {file = "sphinxcontrib_htmlhelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:3c0bc24a2c41e340ac37c85ced6dafc879ab485c095b1d65d2461ac2f7cca86f"},
+]
+sphinxcontrib-jsmath = [
+    {file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
+    {file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
+]
+sphinxcontrib-qthelp = [
+    {file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
+    {file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},
+]
+sphinxcontrib-serializinghtml = [
+    {file = "sphinxcontrib-serializinghtml-1.1.4.tar.gz", hash = "sha256:eaa0eccc86e982a9b939b2b82d12cc5d013385ba5eadcc7e4fed23f4405f77bc"},
+    {file = "sphinxcontrib_serializinghtml-1.1.4-py2.py3-none-any.whl", hash = "sha256:f242a81d423f59617a8e5cf16f5d4d74e28ee9a66f9e5b637a18082991db5a9a"},
+]
+toml = [
+    {file = "toml-0.10.1-py2.py3-none-any.whl", hash = "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"},
+    {file = "toml-0.10.1.tar.gz", hash = "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f"},
+]
+tox = [
+    {file = "tox-3.16.1-py2.py3-none-any.whl", hash = "sha256:60c3793f8ab194097ec75b5a9866138444f63742b0f664ec80be1222a40687c5"},
+    {file = "tox-3.16.1.tar.gz", hash = "sha256:9a746cda9cadb9e1e05c7ab99f98cfcea355140d2ecac5f97520be94657c3bc7"},
+]
+typed-ast = [
+    {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3"},
+    {file = "typed_ast-1.4.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb"},
+    {file = "typed_ast-1.4.1-cp35-cp35m-win32.whl", hash = "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919"},
+    {file = "typed_ast-1.4.1-cp35-cp35m-win_amd64.whl", hash = "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01"},
+    {file = "typed_ast-1.4.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75"},
+    {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652"},
+    {file = "typed_ast-1.4.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"},
+    {file = "typed_ast-1.4.1-cp36-cp36m-win32.whl", hash = "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1"},
+    {file = "typed_ast-1.4.1-cp36-cp36m-win_amd64.whl", hash = "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa"},
+    {file = "typed_ast-1.4.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614"},
+    {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41"},
+    {file = "typed_ast-1.4.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b"},
+    {file = "typed_ast-1.4.1-cp37-cp37m-win32.whl", hash = "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe"},
+    {file = "typed_ast-1.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355"},
+    {file = "typed_ast-1.4.1-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6"},
+    {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907"},
+    {file = "typed_ast-1.4.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d"},
+    {file = "typed_ast-1.4.1-cp38-cp38-win32.whl", hash = "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c"},
+    {file = "typed_ast-1.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4"},
+    {file = "typed_ast-1.4.1-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34"},
+    {file = "typed_ast-1.4.1.tar.gz", hash = "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b"},
+]
+typing-extensions = [
+    {file = "typing_extensions-3.7.4.2-py2-none-any.whl", hash = "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"},
+    {file = "typing_extensions-3.7.4.2-py3-none-any.whl", hash = "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5"},
+    {file = "typing_extensions-3.7.4.2.tar.gz", hash = "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae"},
+]
+urllib3 = [
+    {file = "urllib3-1.25.9-py2.py3-none-any.whl", hash = "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"},
+    {file = "urllib3-1.25.9.tar.gz", hash = "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527"},
+]
+virtualenv = [
+    {file = "virtualenv-20.0.25-py2.py3-none-any.whl", hash = "sha256:ffffcb3c78a671bb3d590ac3bc67c081ea2188befeeb058870cba13e7f82911b"},
+    {file = "virtualenv-20.0.25.tar.gz", hash = "sha256:f332ba0b2dfbac9f6b1da9f11224f0036b05cdb4df23b228527c2a2d5504aeed"},
+]
+wcwidth = [
+    {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
+    {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
+]
+zipp = [
+    {file = "zipp-3.1.0-py3-none-any.whl", hash = "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b"},
+    {file = "zipp-3.1.0.tar.gz", hash = "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"},
+]
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..b2eadf7
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,56 @@
+[tool.poetry]
+name = "graphql-core"
+version = "3.1.2"
+description = """
+GraphQL-core is a Python port of GraphQL.js,
+ the JavaScript reference implementation for GraphQL."""
+license = "MIT"
+authors = [
+    "Christoph Zwerschke <cito@online.de>"
+]
+readme = "README.md"
+homepage = "https://github.com/graphql-python/graphql-core"
+repository = "https://github.com/graphql-python/graphql-core"
+documentation = "https://graphql-core-3.readthedocs.io/"
+keywords = ["graphql"]
+classifiers = [
+    "Development Status :: 5 - Production/Stable",
+    "Intended Audience :: Developers",
+    "License :: OSI Approved :: MIT License",
+    "Programming Language :: Python :: 3",
+    "Programming Language :: Python :: 3.6",
+    "Programming Language :: Python :: 3.7",
+    "Programming Language :: Python :: 3.8"
+]
+packages = [
+    { include = "graphql", from = "src" },
+    { include = "tests", format = "sdist" },
+    { include = "docs", format = "sdist" },
+]
+
+[tool.poetry.dependencies]
+python = "^3.6"
+
+[tool.poetry.dev-dependencies]
+pytest = "^5.4"
+pytest-asyncio = ">=0.14,<1"
+pytest-benchmark = "^3.2"
+pytest-cov = "^2.10"
+pytest-describe = "^1.0"
+pytest-timeout = "^1.4"
+black = "19.10b0"
+flake8 = "^3.8"
+mypy = "0.782"
+codecov = "^2"
+sphinx = "^2.4"
+sphinx_rtd_theme = ">=0.4,<1"
+check-manifest = "0.40"
+bump2version = ">=1.0,<2"
+tox = "^3.16"
+
+[tool.black]
+target-version = ['py36', 'py37', 'py38']
+
+[build-system]
+requires = ["poetry>=1,<2"]
+build-backend = "poetry.masonry.api"
diff --git a/scripts/ast.ast b/scripts/ast.ast
deleted file mode 100644
index 7b9aa67..0000000
--- a/scripts/ast.ast
+++ /dev/null
@@ -1,176 +0,0 @@
-# Mini-language for AST definition.
-# All AST nodes extend AstNode.
-# All AST nodes are visible.
-# All AST fields have a getter and a setter and are a constructor argument.
-# We have concrete types (code T) and unions (code U).
-# S for singular field, P for plural, ? for nullable.
-# O for option in a union.
-# Scalar type ontology: string, boolean
-
-# Location tracking for Identifier is probably useful for error messages.
-U Definition
-O OperationDefinition
-O FragmentDefinition
-
-T Document
-P Definition definitions
-
-T OperationDefinition
-S OperationKind operation
-S? Name name
-P? VariableDefinition variableDefinitions
-P? Directive directives
-S SelectionSet selectionSet
-
-T VariableDefinition
-S Variable variable
-S Type type
-S? Value defaultValue
-
-T SelectionSet
-P Selection selections
-
-U Selection
-O Field
-O FragmentSpread
-O InlineFragment
-
-T Field
-S? Name alias
-S Name name
-P? Argument arguments
-P? Directive directives
-S? SelectionSet selectionSet
-
-T Argument
-S Name name
-S Value value
-
-T FragmentSpread
-S Name name
-P? Directive directives
-
-T InlineFragment
-S NamedType typeCondition
-P? Directive directives
-S SelectionSet selectionSet
-
-T FragmentDefinition
-S Name name
-S NamedType typeCondition
-P? Directive directives
-S SelectionSet selectionSet
-
-U Value
-O Variable
-O IntValue
-O FloatValue
-O StringValue
-O BooleanValue
-O EnumValue
-O ArrayValue
-O ObjectValue
-
-T Variable
-S Name name
-
-T IntValue
-S string value
-
-T FloatValue
-S string value
-
-T StringValue
-S string value
-
-T BooleanValue
-S boolean value
-
-T EnumValue
-S string value
-
-T ArrayValue
-P Value values
-
-T ObjectValue
-P ObjectField fields
-
-T ObjectField
-S Name name
-S Value value
-
-T Directive
-S Name name
-P? Argument arguments
-
-U Type
-O NamedType
-O ListType
-O NonNullType
-
-T NamedType
-S Name name
-
-T ListType
-S Type type
-
-T NonNullType
-# JS version prohibits nesting nonnull in nonnull, we can't because we
-# can't support multiple unions. Fix?
-S Type type
-
-T Name
-S string value
-
-# Implementation of Schema Parser AST Types
-# This is not included in libgraphqlparser.
-# Generated from https://github.com/graphql/graphql-js/blob/master/src/language/ast.js @ 3f1f9f5
-
-U TypeDefinition
-O ObjectTypeDefinition
-O InterfaceTypeDefinition
-O UnionTypeDefinition
-O ScalarTypeDefinition
-O EnumTypeDefinition
-O InputObjectTypeDefinition
-O TypeExtensionDefinition
-
-T ObjectTypeDefinition
-S Name name
-P? NamedType interfaces
-P FieldDefinition fields
-
-T FieldDefinition
-S Name name
-P InputValueDefinition arguments
-S Type type
-
-T InputValueDefinition
-S Name name
-S Type type
-S? Value defaultValue
-
-T InterfaceTypeDefinition
-S Name name
-P FieldDefinition fields
-
-T UnionTypeDefinition
-S Name name
-P NamedType types
-
-T ScalarTypeDefinition
-S Name name
-
-T EnumTypeDefinition
-S Name name
-P EnumValueDefinition values
-
-T EnumValueDefinition
-S Name name
-
-T InputObjectTypeDefinition
-S Name name
-P InputValueDefinition fields
-
-T TypeExtensionDefinition
-S ObjectTypeDefinition definition
diff --git a/scripts/casing.py b/scripts/casing.py
deleted file mode 100644
index 53c6040..0000000
--- a/scripts/casing.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright (c) 2015, Facebook, Inc.
-# All rights reserved.
-#
-# This source code is licensed under the BSD-style license found in the
-# LICENSE file in the root directory of this source tree. An additional grant
-# of patent rights can be found in the PATENTS file in the same directory.
-
-
-def title(s):
-    """Capitalize the first character of s."""
-    return s[0].capitalize() + s[1:]
-
-
-def camel(s):
-    """Lowercase the first character of s."""
-    return s[0].lower() + s[1:]
-
-
-def snake(s):
-    """Convert from title or camelCase to snake_case."""
-    if len(s) < 2:
-        return s.lower()
-    out = s[0].lower()
-    for c in s[1:]:
-        if c.isupper():
-            out += "_"
-            c = c.lower()
-        out += c
-    return out
diff --git a/scripts/fb_ast.py b/scripts/fb_ast.py
deleted file mode 100644
index f91f2ae..0000000
--- a/scripts/fb_ast.py
+++ /dev/null
@@ -1,79 +0,0 @@
-#!/usr/bin/env python
-# Copyright (c) 2015, Facebook, Inc.
-# All rights reserved.
-#
-# This source code is licensed under the BSD-style license found in the
-# LICENSE file in the root directory of this source tree. An additional grant
-# of patent rights can be found in the PATENTS file in the same directory.
-
-from importlib import import_module
-
-
-def load_lang(lang):
-    return import_module(lang).Printer()
-
-
-def print_ast(lang_module, input_file):
-    lang_module.start_file()
-    line = input_file.readline()
-
-    while line:
-        line = line.strip()
-        if line.startswith("#") or not line:
-            line = input_file.readline()
-            continue
-
-        code, rest = line.split(None, 1)
-
-        if code[0] == "T":
-            lang_module.start_type(rest)
-            field_line = input_file.readline().strip()
-            while field_line:
-                if field_line.startswith("#"):
-                    field_line = input_file.readline().strip()
-                    continue
-
-                field_kind, field_type, field_name = field_line.split()
-                nullable = len(field_kind) > 1 and field_kind[1] == "?"
-
-                if field_kind[0] == "S":
-                    plural = False
-
-                elif field_kind[0] == "P":
-                    plural = True
-
-                else:
-                    raise Exception("Unknown field kind: " + field_kind)
-
-                lang_module.field(field_type, field_name, nullable, plural)
-                field_line = input_file.readline().strip()
-
-            lang_module.end_type(rest)
-
-        elif code[0] == "U":
-            lang_module.start_union(rest)
-            field_line = input_file.readline().strip()
-            while field_line:
-                option_code, option_type = field_line.split()
-                if option_code != "O":
-                    raise Exception("Unknown code in union: " + option_code)
-
-                lang_module.union_option(option_type)
-                field_line = input_file.readline().strip()
-
-            lang_module.end_union(rest)
-
-        line = input_file.readline()
-
-    lang_module.end_file()
-
-
-if __name__ == "__main__":
-    import sys
-
-    lang = sys.argv[1]
-    filename = sys.argv[2]
-
-    lang_module = load_lang(lang)
-
-    print_ast(lang_module, open(filename, "r"))
diff --git a/scripts/generate_ast.py b/scripts/generate_ast.py
deleted file mode 100644
index 2e62a28..0000000
--- a/scripts/generate_ast.py
+++ /dev/null
@@ -1,184 +0,0 @@
-from casing import snake
-
-if __name__ == "__main__":
-    import os.path
-    import sys
-    import subprocess
-
-    project_root = os.path.join(os.path.dirname(__file__), "..")
-    with open(os.path.join(project_root, "graphql/core/language/ast.py"), "w") as fp:
-        process = subprocess.Popen(
-            ["python", "./fb_ast.py", "generate_ast", "./ast.ast"],
-            stdout=fp,
-            cwd=os.path.join(project_root, "scripts"),
-            env={"PYTHONPATH": "."},
-        )
-        sys.exit(process.wait())
-
-
-# Fix inconsistencies between libgraphqlparser and graphql-js
-REMAP_TYPES = {"ArrayValue": "ListValue"}
-
-
-def remap_type(typename):
-    return REMAP_TYPES.get(typename, typename)
-
-
-class Printer(object):
-    def __init__(self):
-        self._current_union = None
-        self._parent_types = {}
-        self._fields = []
-
-    def start_file(self):
-        print(
-            """# This is autogenerated code. DO NOT change this manually.
-# Run scripts/generate_ast.py to generate this file.
-
-
-class Node(object):
-    __slots__ = ()"""
-        )
-
-    def end_file(self):
-        pass
-
-    def start_type(self, name):
-        name = remap_type(name)
-        parent_type = self._parent_types.get(name, "Node")
-        print(
-            """
-
-class {name}({parent_type}):""".format(
-                name=name, parent_type=parent_type
-            )
-        )
-
-    def field(self, type, name, nullable, plural):
-        type = remap_type(type)
-        self._fields.append((type, name, nullable, plural))
-
-    def end_type(self, typename):
-        typename = remap_type(typename)
-        self._print_slots()
-        self._print_fields()
-        self._print_ctor()
-        self._print_comparator(typename)
-        self._print_repr(typename)
-        self._print_copy(typename)
-        self._print_hash()
-        self._fields = []
-
-    def _print_fields(self):
-        fields = ", ".join(
-            "'" + snake(name) + "'" for (type, name, nullable, plural) in self._fields
-        )
-        print("""    _fields = ({},)""".format(fields))
-
-    def _print_slots(self):
-        slots = ", ".join(
-            "'" + snake(name) + "'" for (type, name, nullable, plural) in self._fields
-        )
-        print("""    __slots__ = ('loc', {slots},)""".format(slots=slots))
-
-    def _print_ctor(self):
-        fields = [field for field in self._fields if not field[2]] + [
-            field for field in self._fields if field[2]
-        ]
-        ctor_args = ", ".join(
-            snake(name) + ("=None" if nullable else "")
-            for (type, name, nullable, plural) in fields
-        )
-        print(
-            """
-    def __init__(self, {ctor_args}, loc=None):
-        self.loc = loc""".format(
-                ctor_args=ctor_args
-            )
-        )
-        for type, name, nullable, plural in self._fields:
-            print("""        self.{name} = {name}""".format(name=snake(name)))
-
-    def _print_comparator(self, typename):
-        print(
-            """
-    def __eq__(self, other):
-        return (
-            self is other or (
-                isinstance(other, {typename}) and
-                self.loc == other.loc and""".format(
-                typename=typename
-            )
-        )
-        print(
-            " and\n".join(
-                """                self.{name} == other.{name}""".format(
-                    name=snake(name)
-                )
-                for type, name, nullable, plural in self._fields
-            )
-        )
-        print("            )")
-        print("        )")
-
-    def _print_copy(self, typename):
-        fields = [field for field in self._fields if not field[2]] + [
-            field for field in self._fields if field[2]
-        ]
-        args = "\n".join(
-            """            self.{},""".format(snake(name))
-            for (type, name, nullable, plural) in fields
-        )
-        print(
-            """
-    def __copy__(self):
-        return type(self)(
-{}
-            self.loc
-        )""".format(
-                args
-            )
-        )
-
-    def _print_repr(self, typename):
-        print(
-            """
-    def __repr__(self):
-        return ('{typename}(' """.rstrip().format(
-                typename=typename
-            )
-        )
-        first = True
-        for type, name, nullable, plural in self._fields:
-            print(
-                "                '{comma}{name}={{self.{name}!r}}'".format(
-                    comma=", " if not first else "", name=snake(name)
-                )
-            )
-            first = False
-        print("""                ')').format(self=self)""")
-
-    def _print_hash(self):
-        print(
-            """
-    def __hash__(self):
-        return id(self)"""
-        )
-
-    def start_union(self, name):
-        self._current_union = name
-        print(
-            """
-
-class {name}(Node):
-    __slots__ = ()""".format(
-                name=name
-            )
-        )
-
-    def union_option(self, option):
-        option = remap_type(option)
-        self._parent_types[option] = self._current_union
-
-    def end_union(self, name):
-        self._current_union = None
diff --git a/setup.cfg b/setup.cfg
index a65570f..24874d7 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,15 +1,12 @@
-[flake8]
-exclude = tests,scripts,setup.py,docs,graphql/execution/executors/asyncio_utils.py,conftest.py
-max-line-length = 160
-
-[coverage:run]
-omit=graphql/backend/quiver_cloud.py,tests,*/tests/*
-
-[coverage:report]
-omit=graphql/backend/quiver_cloud.py,tests,*/tests/*
-
 [bdist_wheel]
-universal=1
+python-tag = py3
+
+[aliases]
+test = pytest
 
-[mypy-graphql.*.tests.*]
-ignore_errors=true
+[tool:pytest]
+# Only run benchmarks as tests.
+# To actually run the benchmarks, use --benchmark-enable on the command line.
+# To run the slow tests (fuzzing), add --run-slow on the command line.
+addopts = --benchmark-disable
+timeout = 100
diff --git a/setup.py b/setup.py
index 64bd95e..ed1117e 100644
--- a/setup.py
+++ b/setup.py
@@ -1,85 +1,40 @@
+from re import search
 from setuptools import setup, find_packages
-from setuptools.command.test import test as TestCommand
-import sys
-import ast
-import re
 
-_version_re = re.compile(r"VERSION\s+=\s+(.*)")
-
-with open("graphql/__init__.py", "rb") as f:
-    version = ast.literal_eval(_version_re.search(f.read().decode("utf-8")).group(1))
-
-path_copy = sys.path[:]
-
-sys.path.append("graphql")
-try:
-    from pyutils.version import get_version
-
-    version = get_version(version)
-except Exception:
-    version = ".".join([str(v) for v in version])
-
-sys.path[:] = path_copy
-
-install_requires = ["six>=1.10.0", "promise>=2.1", "rx>=1.6,<3"]
-
-tests_requires = [
-    "pytest>=3.3,<4.0",
-    "pytest-django==2.9.1",
-    "pytest-cov==2.3.1",
-    "coveralls",
-    "gevent>=1.1",
-    "six>=1.10.0",
-    "pytest-benchmark==3.0.0",
-    "pytest-mock==1.2",
-]
-
-
-class PyTest(TestCommand):
-    def finalize_options(self):
-        TestCommand.finalize_options(self)
-        self.test_args = ["graphql", "-vrsx"]
-        self.test_suite = True
-
-    def run_tests(self):
-        # import here, cause outside the eggs aren't loaded
-        import pytest
-
-        errno = pytest.main(self.test_args)
-        sys.exit(errno)
+with open("src/graphql/version.py") as version_file:
+    version = search('version = "(.*)"', version_file.read()).group(1)
 
+with open("README.md") as readme_file:
+    readme = readme_file.read()
 
 setup(
     name="graphql-core",
     version=version,
-    description="GraphQL implementation for Python",
-    long_description=open("README.md").read(),
+    description="GraphQL implementation for Python, a port of GraphQL.js,"
+    " the JavaScript reference implementation for GraphQL.",
+    long_description=readme,
     long_description_content_type="text/markdown",
+    keywords="graphql",
     url="https://github.com/graphql-python/graphql-core",
-    download_url="https://github.com/graphql-python/graphql-core/releases",
-    author="Syrus Akbary, Jake Heinz, Taeho Kim",
-    author_email="me@syrusakbary.com",
-    license="MIT",
+    author="Christoph Zwerschke",
+    author_email="cito@online.de",
+    license="MIT license",
     classifiers=[
         "Development Status :: 5 - Production/Stable",
         "Intended Audience :: Developers",
         "Topic :: Software Development :: Libraries",
-        "Programming Language :: Python :: 2",
-        "Programming Language :: Python :: 2.7",
+        "License :: OSI Approved :: MIT License",
         "Programming Language :: Python :: 3",
-        "Programming Language :: Python :: 3.4",
-        "Programming Language :: Python :: 3.5",
         "Programming Language :: Python :: 3.6",
         "Programming Language :: Python :: 3.7",
-        "Programming Language :: Python :: Implementation :: PyPy",
-        "License :: OSI Approved :: MIT License",
-        "Topic :: Database :: Front-Ends",
-        "Topic :: Internet :: WWW/HTTP",
+        "Programming Language :: Python :: 3.8",
     ],
-    keywords="api graphql protocol rest",
-    packages=find_packages(exclude=["tests", "tests_py35", "tests.*", "tests_py35.*"]),
-    install_requires=install_requires,
-    tests_require=tests_requires,
-    cmdclass={"test": PyTest},
-    extras_require={"gevent": ["gevent>=1.1"], "test": tests_requires},
+    install_requires=[],
+    python_requires=">=3.6,<4",
+    packages=find_packages("src"),
+    package_dir={"": "src"},
+    # PEP-561: https://www.python.org/dev/peps/pep-0561/
+    package_data={"graphql": ["py.typed"]},
+    include_package_data=True,
+    zip_safe=False,
 )
diff --git a/src/graphql/__init__.py b/src/graphql/__init__.py
new file mode 100644
index 0000000..30b1e1d
--- /dev/null
+++ b/src/graphql/__init__.py
@@ -0,0 +1,697 @@
+"""GraphQL-core
+
+The primary :mod:`graphql` package includes everything you need to define a GraphQL
+schema and fulfill GraphQL requests.
+
+GraphQL-core provides a reference implementation for the GraphQL specification
+but is also a useful utility for operating on GraphQL files and building sophisticated
+tools.
+
+This top-level package exports a general purpose function for fulfilling all steps
+of the GraphQL specification in a single operation, but also includes utilities
+for every part of the GraphQL specification:
+
+  - Parsing the GraphQL language.
+  - Building a GraphQL type schema.
+  - Validating a GraphQL request against a type schema.
+  - Executing a GraphQL request against a type schema.
+
+This also includes utility functions for operating on GraphQL types and GraphQL
+documents to facilitate building tools.
+
+You may also import from each sub-package directly. For example, the following two
+import statements are equivalent::
+
+    from graphql import parse
+    from graphql.language import parse
+
+The sub-packages of GraphQL-core 3 are:
+
+  - :mod:`graphql.language`: Parse and operate on the GraphQL language.
+  - :mod:`graphql.type`: Define GraphQL types and schema.
+  - :mod:`graphql.validation`: The Validation phase of fulfilling a GraphQL result.
+  - :mod:`graphql.execution`: The Execution phase of fulfilling a GraphQL request.
+  - :mod:`graphql.error`: Creating and formatting GraphQL errors.
+  - :mod:`graphql.utilities`:
+    Common useful computations upon the GraphQL language and type objects.
+  - :mod:`graphql.subscription`: Subscribe to data updates.
+"""
+
+# The GraphQL-core 3 and GraphQL.js version info.
+
+from .version import version, version_info, version_js, version_info_js
+
+# The primary entry point into fulfilling a GraphQL request.
+
+from .graphql import graphql, graphql_sync
+
+# Create and operate on GraphQL type definitions and schema.
+from .type import (
+    # Definitions
+    GraphQLSchema,
+    GraphQLDirective,
+    GraphQLScalarType,
+    GraphQLObjectType,
+    GraphQLInterfaceType,
+    GraphQLUnionType,
+    GraphQLEnumType,
+    GraphQLInputObjectType,
+    GraphQLList,
+    GraphQLNonNull,
+    # Standard GraphQL Scalars
+    specified_scalar_types,
+    GraphQLInt,
+    GraphQLFloat,
+    GraphQLString,
+    GraphQLBoolean,
+    GraphQLID,
+    # Built-in Directives defined by the Spec
+    specified_directives,
+    GraphQLIncludeDirective,
+    GraphQLSkipDirective,
+    GraphQLDeprecatedDirective,
+    GraphQLSpecifiedByDirective,
+    # "Enum" of Type Kinds
+    TypeKind,
+    # Constant Deprecation Reason
+    DEFAULT_DEPRECATION_REASON,
+    # GraphQL Types for introspection.
+    introspection_types,
+    # Meta-field definitions.
+    SchemaMetaFieldDef,
+    TypeMetaFieldDef,
+    TypeNameMetaFieldDef,
+    # Predicates
+    is_schema,
+    is_directive,
+    is_type,
+    is_scalar_type,
+    is_object_type,
+    is_interface_type,
+    is_union_type,
+    is_enum_type,
+    is_input_object_type,
+    is_list_type,
+    is_non_null_type,
+    is_input_type,
+    is_output_type,
+    is_leaf_type,
+    is_composite_type,
+    is_abstract_type,
+    is_wrapping_type,
+    is_nullable_type,
+    is_named_type,
+    is_required_argument,
+    is_required_input_field,
+    is_specified_scalar_type,
+    is_introspection_type,
+    is_specified_directive,
+    # Assertions
+    assert_schema,
+    assert_directive,
+    assert_type,
+    assert_scalar_type,
+    assert_object_type,
+    assert_interface_type,
+    assert_union_type,
+    assert_enum_type,
+    assert_input_object_type,
+    assert_list_type,
+    assert_non_null_type,
+    assert_input_type,
+    assert_output_type,
+    assert_leaf_type,
+    assert_composite_type,
+    assert_abstract_type,
+    assert_wrapping_type,
+    assert_nullable_type,
+    assert_named_type,
+    # Un-modifiers
+    get_nullable_type,
+    get_named_type,
+    # Validate GraphQL schema.
+    validate_schema,
+    assert_valid_schema,
+    # Types
+    GraphQLType,
+    GraphQLInputType,
+    GraphQLOutputType,
+    GraphQLLeafType,
+    GraphQLCompositeType,
+    GraphQLAbstractType,
+    GraphQLWrappingType,
+    GraphQLNullableType,
+    GraphQLNamedType,
+    Thunk,
+    GraphQLArgument,
+    GraphQLArgumentMap,
+    GraphQLEnumValue,
+    GraphQLEnumValueMap,
+    GraphQLField,
+    GraphQLFieldMap,
+    GraphQLFieldResolver,
+    GraphQLInputField,
+    GraphQLInputFieldMap,
+    GraphQLScalarSerializer,
+    GraphQLScalarValueParser,
+    GraphQLScalarLiteralParser,
+    GraphQLIsTypeOfFn,
+    GraphQLResolveInfo,
+    ResponsePath,
+    GraphQLTypeResolver,
+)
+
+# Parse and operate on GraphQL language source files.
+from .language import (
+    Source,
+    get_location,
+    # Print source location
+    print_location,
+    print_source_location,
+    # Lex
+    Lexer,
+    TokenKind,
+    # Parse
+    parse,
+    parse_value,
+    parse_type,
+    # Print
+    print_ast,
+    # Visit
+    visit,
+    ParallelVisitor,
+    Visitor,
+    VisitorAction,
+    BREAK,
+    SKIP,
+    REMOVE,
+    IDLE,
+    DirectiveLocation,
+    # Predicates
+    is_definition_node,
+    is_executable_definition_node,
+    is_selection_node,
+    is_value_node,
+    is_type_node,
+    is_type_system_definition_node,
+    is_type_definition_node,
+    is_type_system_extension_node,
+    is_type_extension_node,
+    # Types
+    SourceLocation,
+    Location,
+    Token,
+    # AST nodes
+    Node,
+    # Each kind of AST node
+    NameNode,
+    DocumentNode,
+    DefinitionNode,
+    ExecutableDefinitionNode,
+    OperationDefinitionNode,
+    OperationType,
+    VariableDefinitionNode,
+    VariableNode,
+    SelectionSetNode,
+    SelectionNode,
+    FieldNode,
+    ArgumentNode,
+    FragmentSpreadNode,
+    InlineFragmentNode,
+    FragmentDefinitionNode,
+    ValueNode,
+    IntValueNode,
+    FloatValueNode,
+    StringValueNode,
+    BooleanValueNode,
+    NullValueNode,
+    EnumValueNode,
+    ListValueNode,
+    ObjectValueNode,
+    ObjectFieldNode,
+    DirectiveNode,
+    TypeNode,
+    NamedTypeNode,
+    ListTypeNode,
+    NonNullTypeNode,
+    TypeSystemDefinitionNode,
+    SchemaDefinitionNode,
+    OperationTypeDefinitionNode,
+    TypeDefinitionNode,
+    ScalarTypeDefinitionNode,
+    ObjectTypeDefinitionNode,
+    FieldDefinitionNode,
+    InputValueDefinitionNode,
+    InterfaceTypeDefinitionNode,
+    UnionTypeDefinitionNode,
+    EnumTypeDefinitionNode,
+    EnumValueDefinitionNode,
+    InputObjectTypeDefinitionNode,
+    DirectiveDefinitionNode,
+    TypeSystemExtensionNode,
+    SchemaExtensionNode,
+    TypeExtensionNode,
+    ScalarTypeExtensionNode,
+    ObjectTypeExtensionNode,
+    InterfaceTypeExtensionNode,
+    UnionTypeExtensionNode,
+    EnumTypeExtensionNode,
+    InputObjectTypeExtensionNode,
+)
+
+# Execute GraphQL documents.
+from .execution import (
+    execute,
+    default_field_resolver,
+    default_type_resolver,
+    get_directive_values,
+    # Types
+    ExecutionContext,
+    ExecutionResult,
+    # Middleware
+    Middleware,
+    MiddlewareManager,
+)
+
+from .subscription import subscribe, create_source_event_stream
+
+
+# Validate GraphQL queries.
+from .validation import (
+    validate,
+    ValidationContext,
+    ValidationRule,
+    ASTValidationRule,
+    SDLValidationRule,
+    # All validation rules in the GraphQL Specification.
+    specified_rules,
+    # Individual validation rules.
+    ExecutableDefinitionsRule,
+    FieldsOnCorrectTypeRule,
+    FragmentsOnCompositeTypesRule,
+    KnownArgumentNamesRule,
+    KnownDirectivesRule,
+    KnownFragmentNamesRule,
+    KnownTypeNamesRule,
+    LoneAnonymousOperationRule,
+    NoFragmentCyclesRule,
+    NoUndefinedVariablesRule,
+    NoUnusedFragmentsRule,
+    NoUnusedVariablesRule,
+    OverlappingFieldsCanBeMergedRule,
+    PossibleFragmentSpreadsRule,
+    ProvidedRequiredArgumentsRule,
+    ScalarLeafsRule,
+    SingleFieldSubscriptionsRule,
+    UniqueArgumentNamesRule,
+    UniqueDirectivesPerLocationRule,
+    UniqueFragmentNamesRule,
+    UniqueInputFieldNamesRule,
+    UniqueOperationNamesRule,
+    UniqueVariableNamesRule,
+    ValuesOfCorrectTypeRule,
+    VariablesAreInputTypesRule,
+    VariablesInAllowedPositionRule,
+    # SDL-specific validation rules
+    LoneSchemaDefinitionRule,
+    UniqueOperationTypesRule,
+    UniqueTypeNamesRule,
+    UniqueEnumValueNamesRule,
+    UniqueFieldDefinitionNamesRule,
+    UniqueDirectiveNamesRule,
+    PossibleTypeExtensionsRule,
+)
+
+# Create, format, and print GraphQL errors.
+from .error import (
+    GraphQLError,
+    GraphQLSyntaxError,
+    located_error,
+    format_error,
+    print_error,
+)
+
+# Utilities for operating on GraphQL type schema and parsed sources.
+from .utilities import (
+    # Produce the GraphQL query recommended for a full schema introspection.
+    # Accepts optional IntrospectionOptions.
+    get_introspection_query,
+    # Get the target Operation from a Document.
+    get_operation_ast,
+    # Get the Type for the target Operation AST.
+    get_operation_root_type,
+    # Convert a GraphQLSchema to an IntrospectionQuery.
+    introspection_from_schema,
+    # Build a GraphQLSchema from an introspection result.
+    build_client_schema,
+    # Build a GraphQLSchema from a parsed GraphQL Schema language AST.
+    build_ast_schema,
+    # Build a GraphQLSchema from a GraphQL schema language document.
+    build_schema,
+    # @deprecated: Get the description from a schema AST node.
+    get_description,
+    # Extend an existing GraphQLSchema from a parsed GraphQL Schema language AST.
+    extend_schema,
+    # Sort a GraphQLSchema.
+    lexicographic_sort_schema,
+    # Print a GraphQLSchema to GraphQL Schema language.
+    print_schema,
+    # Print a GraphQLType to GraphQL Schema language.
+    print_type,
+    # Prints the built-in introspection schema in the Schema Language format.
+    print_introspection_schema,
+    # Create a GraphQLType from a GraphQL language AST.
+    type_from_ast,
+    # Create a Python value from a GraphQL language AST with a Type.
+    value_from_ast,
+    # Create a Python value from a GraphQL language AST without a Type.
+    value_from_ast_untyped,
+    # Create a GraphQL language AST from a Python value.
+    ast_from_value,
+    # A helper to use within recursive-descent visitors which need to be aware of the
+    # GraphQL type system.
+    TypeInfo,
+    TypeInfoVisitor,
+    # Coerce a Python value to a GraphQL type, or produce errors.
+    coerce_input_value,
+    # Concatenates multiple ASTs together.
+    concat_ast,
+    # Separate an AST into an AST per Operation.
+    separate_operations,
+    # Strip characters that are not significant to the validity or execution
+    # of a GraphQL document.
+    strip_ignored_characters,
+    # Comparators for types
+    is_equal_type,
+    is_type_sub_type_of,
+    do_types_overlap,
+    # Assert a string is a valid GraphQL name.
+    assert_valid_name,
+    # Determine if a string is a valid GraphQL name.
+    is_valid_name_error,
+    # Compare two GraphQLSchemas and detect breaking changes.
+    BreakingChange,
+    BreakingChangeType,
+    DangerousChange,
+    DangerousChangeType,
+    find_breaking_changes,
+    find_dangerous_changes,
+)
+
+# Utilities for compatibility with the Python language.
+from .pyutils import Undefined, UndefinedType
+
+INVALID = Undefined  # deprecated alias
+
+# The GraphQL-core version info.
+__version__ = version
+__version_info__ = version_info
+
+# The GraphQL.js version info.
+__version_js__ = version_js
+__version_info_js__ = version_info_js
+
+
+__all__ = [
+    "version",
+    "version_info",
+    "version_js",
+    "version_info_js",
+    "graphql",
+    "graphql_sync",
+    "GraphQLSchema",
+    "GraphQLDirective",
+    "GraphQLScalarType",
+    "GraphQLObjectType",
+    "GraphQLInterfaceType",
+    "GraphQLUnionType",
+    "GraphQLEnumType",
+    "GraphQLInputObjectType",
+    "GraphQLList",
+    "GraphQLNonNull",
+    "specified_scalar_types",
+    "GraphQLInt",
+    "GraphQLFloat",
+    "GraphQLString",
+    "GraphQLBoolean",
+    "GraphQLID",
+    "specified_directives",
+    "GraphQLIncludeDirective",
+    "GraphQLSkipDirective",
+    "GraphQLDeprecatedDirective",
+    "GraphQLSpecifiedByDirective",
+    "TypeKind",
+    "DEFAULT_DEPRECATION_REASON",
+    "introspection_types",
+    "SchemaMetaFieldDef",
+    "TypeMetaFieldDef",
+    "TypeNameMetaFieldDef",
+    "is_schema",
+    "is_directive",
+    "is_type",
+    "is_scalar_type",
+    "is_object_type",
+    "is_interface_type",
+    "is_union_type",
+    "is_enum_type",
+    "is_input_object_type",
+    "is_list_type",
+    "is_non_null_type",
+    "is_input_type",
+    "is_output_type",
+    "is_leaf_type",
+    "is_composite_type",
+    "is_abstract_type",
+    "is_wrapping_type",
+    "is_nullable_type",
+    "is_named_type",
+    "is_required_argument",
+    "is_required_input_field",
+    "is_specified_scalar_type",
+    "is_introspection_type",
+    "is_specified_directive",
+    "assert_schema",
+    "assert_directive",
+    "assert_type",
+    "assert_scalar_type",
+    "assert_object_type",
+    "assert_interface_type",
+    "assert_union_type",
+    "assert_enum_type",
+    "assert_input_object_type",
+    "assert_list_type",
+    "assert_non_null_type",
+    "assert_input_type",
+    "assert_output_type",
+    "assert_leaf_type",
+    "assert_composite_type",
+    "assert_abstract_type",
+    "assert_wrapping_type",
+    "assert_nullable_type",
+    "assert_named_type",
+    "get_nullable_type",
+    "get_named_type",
+    "validate_schema",
+    "assert_valid_schema",
+    "GraphQLType",
+    "GraphQLInputType",
+    "GraphQLOutputType",
+    "GraphQLLeafType",
+    "GraphQLCompositeType",
+    "GraphQLAbstractType",
+    "GraphQLWrappingType",
+    "GraphQLNullableType",
+    "GraphQLNamedType",
+    "Thunk",
+    "GraphQLArgument",
+    "GraphQLArgumentMap",
+    "GraphQLEnumValue",
+    "GraphQLEnumValueMap",
+    "GraphQLField",
+    "GraphQLFieldMap",
+    "GraphQLFieldResolver",
+    "GraphQLInputField",
+    "GraphQLInputFieldMap",
+    "GraphQLScalarSerializer",
+    "GraphQLScalarValueParser",
+    "GraphQLScalarLiteralParser",
+    "GraphQLIsTypeOfFn",
+    "GraphQLResolveInfo",
+    "ResponsePath",
+    "GraphQLTypeResolver",
+    "Source",
+    "get_location",
+    "print_location",
+    "print_source_location",
+    "Lexer",
+    "TokenKind",
+    "parse",
+    "parse_value",
+    "parse_type",
+    "print_ast",
+    "visit",
+    "ParallelVisitor",
+    "TypeInfoVisitor",
+    "Visitor",
+    "VisitorAction",
+    "BREAK",
+    "SKIP",
+    "REMOVE",
+    "IDLE",
+    "DirectiveLocation",
+    "is_definition_node",
+    "is_executable_definition_node",
+    "is_selection_node",
+    "is_value_node",
+    "is_type_node",
+    "is_type_system_definition_node",
+    "is_type_definition_node",
+    "is_type_system_extension_node",
+    "is_type_extension_node",
+    "SourceLocation",
+    "Location",
+    "Token",
+    "Node",
+    "NameNode",
+    "DocumentNode",
+    "DefinitionNode",
+    "ExecutableDefinitionNode",
+    "OperationDefinitionNode",
+    "OperationType",
+    "VariableDefinitionNode",
+    "VariableNode",
+    "SelectionSetNode",
+    "SelectionNode",
+    "FieldNode",
+    "ArgumentNode",
+    "FragmentSpreadNode",
+    "InlineFragmentNode",
+    "FragmentDefinitionNode",
+    "ValueNode",
+    "IntValueNode",
+    "FloatValueNode",
+    "StringValueNode",
+    "BooleanValueNode",
+    "NullValueNode",
+    "EnumValueNode",
+    "ListValueNode",
+    "ObjectValueNode",
+    "ObjectFieldNode",
+    "DirectiveNode",
+    "TypeNode",
+    "NamedTypeNode",
+    "ListTypeNode",
+    "NonNullTypeNode",
+    "TypeSystemDefinitionNode",
+    "SchemaDefinitionNode",
+    "OperationTypeDefinitionNode",
+    "TypeDefinitionNode",
+    "ScalarTypeDefinitionNode",
+    "ObjectTypeDefinitionNode",
+    "FieldDefinitionNode",
+    "InputValueDefinitionNode",
+    "InterfaceTypeDefinitionNode",
+    "UnionTypeDefinitionNode",
+    "EnumTypeDefinitionNode",
+    "EnumValueDefinitionNode",
+    "InputObjectTypeDefinitionNode",
+    "DirectiveDefinitionNode",
+    "TypeSystemExtensionNode",
+    "SchemaExtensionNode",
+    "TypeExtensionNode",
+    "ScalarTypeExtensionNode",
+    "ObjectTypeExtensionNode",
+    "InterfaceTypeExtensionNode",
+    "UnionTypeExtensionNode",
+    "EnumTypeExtensionNode",
+    "InputObjectTypeExtensionNode",
+    "execute",
+    "default_field_resolver",
+    "default_type_resolver",
+    "get_directive_values",
+    "ExecutionContext",
+    "ExecutionResult",
+    "Middleware",
+    "MiddlewareManager",
+    "subscribe",
+    "create_source_event_stream",
+    "validate",
+    "ValidationContext",
+    "ValidationRule",
+    "ASTValidationRule",
+    "SDLValidationRule",
+    "specified_rules",
+    "ExecutableDefinitionsRule",
+    "FieldsOnCorrectTypeRule",
+    "FragmentsOnCompositeTypesRule",
+    "KnownArgumentNamesRule",
+    "KnownDirectivesRule",
+    "KnownFragmentNamesRule",
+    "KnownTypeNamesRule",
+    "LoneAnonymousOperationRule",
+    "NoFragmentCyclesRule",
+    "NoUndefinedVariablesRule",
+    "NoUnusedFragmentsRule",
+    "NoUnusedVariablesRule",
+    "OverlappingFieldsCanBeMergedRule",
+    "PossibleFragmentSpreadsRule",
+    "ProvidedRequiredArgumentsRule",
+    "ScalarLeafsRule",
+    "SingleFieldSubscriptionsRule",
+    "UniqueArgumentNamesRule",
+    "UniqueDirectivesPerLocationRule",
+    "UniqueFragmentNamesRule",
+    "UniqueInputFieldNamesRule",
+    "UniqueOperationNamesRule",
+    "UniqueVariableNamesRule",
+    "ValuesOfCorrectTypeRule",
+    "VariablesAreInputTypesRule",
+    "VariablesInAllowedPositionRule",
+    "LoneSchemaDefinitionRule",
+    "UniqueOperationTypesRule",
+    "UniqueTypeNamesRule",
+    "UniqueEnumValueNamesRule",
+    "UniqueFieldDefinitionNamesRule",
+    "UniqueDirectiveNamesRule",
+    "PossibleTypeExtensionsRule",
+    "GraphQLError",
+    "GraphQLSyntaxError",
+    "located_error",
+    "format_error",
+    "print_error",
+    "get_introspection_query",
+    "get_operation_ast",
+    "get_operation_root_type",
+    "introspection_from_schema",
+    "build_client_schema",
+    "build_ast_schema",
+    "build_schema",
+    "get_description",
+    "extend_schema",
+    "lexicographic_sort_schema",
+    "print_schema",
+    "print_type",
+    "print_introspection_schema",
+    "type_from_ast",
+    "value_from_ast",
+    "value_from_ast_untyped",
+    "ast_from_value",
+    "TypeInfo",
+    "coerce_input_value",
+    "concat_ast",
+    "separate_operations",
+    "strip_ignored_characters",
+    "is_equal_type",
+    "is_type_sub_type_of",
+    "do_types_overlap",
+    "assert_valid_name",
+    "is_valid_name_error",
+    "find_breaking_changes",
+    "find_dangerous_changes",
+    "BreakingChange",
+    "BreakingChangeType",
+    "DangerousChange",
+    "DangerousChangeType",
+    "Undefined",
+    "UndefinedType",
+]
diff --git a/src/graphql/error/__init__.py b/src/graphql/error/__init__.py
new file mode 100644
index 0000000..d1699a9
--- /dev/null
+++ b/src/graphql/error/__init__.py
@@ -0,0 +1,19 @@
+"""GraphQL Errors
+
+The :mod:`graphql.error` package is responsible for creating and formatting GraphQL
+errors.
+"""
+
+from .graphql_error import GraphQLError, format_error, print_error
+
+from .syntax_error import GraphQLSyntaxError
+
+from .located_error import located_error
+
+__all__ = [
+    "GraphQLError",
+    "GraphQLSyntaxError",
+    "format_error",
+    "located_error",
+    "print_error",
+]
diff --git a/src/graphql/error/graphql_error.py b/src/graphql/error/graphql_error.py
new file mode 100644
index 0000000..1e60320
--- /dev/null
+++ b/src/graphql/error/graphql_error.py
@@ -0,0 +1,218 @@
+from sys import exc_info
+from typing import Any, Collection, Dict, List, Optional, Union, TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from ..language.ast import Node  # noqa: F401
+    from ..language.location import SourceLocation  # noqa: F401
+    from ..language.source import Source  # noqa: F401
+
+__all__ = ["GraphQLError", "format_error", "print_error"]
+
+
+class GraphQLError(Exception):
+    """GraphQL Error
+
+    A GraphQLError describes an Error found during the parse, validate, or execute
+    phases of performing a GraphQL operation. In addition to a message, it also includes
+    information about the locations in a GraphQL document and/or execution result that
+    correspond to the Error.
+    """
+
+    message: str
+    """A message describing the Error for debugging purposes
+
+    Note: should be treated as readonly, despite invariant usage.
+    """
+
+    locations: Optional[List["SourceLocation"]]
+    """Source locations
+
+    A list of (line, column) locations within the source GraphQL document which
+    correspond to this error.
+
+    Errors during validation often contain multiple locations, for example to point out
+    two things with the same name. Errors during execution include a single location,
+    the field which produced the error.
+    """
+
+    path: Optional[List[Union[str, int]]]
+    """
+
+    A list of field names and array indexes describing the JSON-path into the execution
+    response which corresponds to this error.
+
+    Only included for errors during execution.
+    """
+
+    nodes: Optional[List["Node"]]
+    """A list of GraphQL AST Nodes corresponding to this error"""
+
+    source: Optional["Source"]
+    """The source GraphQL document for the first location of this error
+
+    Note that if this Error represents more than one node, the source may not represent
+    nodes after the first node.
+    """
+
+    positions: Optional[Collection[int]]
+    """Error positions
+
+    A list of character offsets within the source GraphQL document which correspond
+    to this error.
+    """
+
+    original_error: Optional[Exception]
+    """The original error thrown from a field resolver during execution"""
+
+    extensions: Optional[Dict[str, Any]]
+    """Extension fields to add to the formatted error"""
+
+    __slots__ = (
+        "message",
+        "nodes",
+        "source",
+        "positions",
+        "locations",
+        "path",
+        "original_error",
+        "extensions",
+    )
+
+    __hash__ = Exception.__hash__
+
+    def __init__(
+        self,
+        message: str,
+        nodes: Union[Collection["Node"], "Node", None] = None,
+        source: Optional["Source"] = None,
+        positions: Optional[Collection[int]] = None,
+        path: Optional[Collection[Union[str, int]]] = None,
+        original_error: Optional[Exception] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+    ) -> None:
+        super().__init__(message)
+        self.message = message
+        if nodes and not isinstance(nodes, list):
+            nodes = [nodes]  # type: ignore
+        self.nodes = nodes or None  # type: ignore
+        self.source = source
+        if not source and nodes:
+            node = nodes[0]  # type: ignore
+            if node and node.loc and node.loc.source:
+                self.source = node.loc.source
+        if not positions and nodes:
+            positions = [node.loc.start for node in nodes if node.loc]  # type: ignore
+        self.positions = positions or None
+        if positions and source:
+            locations: Optional[List["SourceLocation"]] = [
+                source.get_location(pos) for pos in positions
+            ]
+        elif nodes:
+            locations = [
+                node.loc.source.get_location(node.loc.start)
+                for node in nodes  # type: ignore
+                if node.loc
+            ]
+        else:
+            locations = None
+        self.locations = locations
+        if path and not isinstance(path, list):
+            path = list(path)
+        self.path = path or None  # type: ignore
+        self.original_error = original_error
+        if original_error:
+            self.__traceback__ = original_error.__traceback__
+            if original_error.__cause__:
+                self.__cause__ = original_error.__cause__
+            elif original_error.__context__:
+                self.__context__ = original_error.__context__
+            if not extensions:
+                try:
+                    # noinspection PyUnresolvedReferences
+                    extensions = original_error.extensions  # type: ignore
+                except AttributeError:
+                    pass
+        self.extensions = extensions or {}
+        if not self.__traceback__:
+            self.__traceback__ = exc_info()[2]
+
+    def __str__(self) -> str:
+        return print_error(self)
+
+    def __repr__(self) -> str:
+        args = [repr(self.message)]
+        if self.locations:
+            args.append(f"locations={self.locations!r}")
+        if self.path:
+            args.append(f"path={self.path!r}")
+        if self.extensions:
+            args.append(f"extensions={self.extensions!r}")
+        return f"{self.__class__.__name__}({', '.join(args)})"
+
+    def __eq__(self, other: Any) -> bool:
+        return (
+            isinstance(other, GraphQLError)
+            and self.__class__ == other.__class__
+            and all(
+                getattr(self, slot) == getattr(other, slot) for slot in self.__slots__
+            )
+        ) or (
+            isinstance(other, dict)
+            and "message" in other
+            and all(
+                slot in self.__slots__ and getattr(self, slot) == other.get(slot)
+                for slot in other
+            )
+        )
+
+    def __ne__(self, other: Any) -> bool:
+        return not self == other
+
+    @property
+    def formatted(self) -> Dict[str, Any]:
+        """Get error formatted according to the specification."""
+        return format_error(self)
+
+
+def print_error(error: GraphQLError) -> str:
+    """Print a GraphQLError to a string.
+
+    Represents useful location information about the error's position in the source.
+    """
+    # Lazy import to avoid a cyclic dependency between error and language
+    from ..language.print_location import print_location, print_source_location
+
+    output = [error.message]
+
+    if error.nodes:
+        for node in error.nodes:
+            if node.loc:
+                output.append(print_location(node.loc))
+    elif error.source and error.locations:
+        source = error.source
+        for location in error.locations:
+            output.append(print_source_location(source, location))
+
+    return "\n\n".join(output)
+
+
+def format_error(error: GraphQLError) -> Dict[str, Any]:
+    """Format a GraphQL error.
+
+    Given a GraphQLError, format it according to the rules described by the "Response
+    Format, Errors" section of the GraphQL Specification.
+    """
+    if not isinstance(error, GraphQLError):
+        raise TypeError("Expected a GraphQLError.")
+    formatted: Dict[str, Any] = dict(  # noqa: E701 (pycqa/flake8#394)
+        message=error.message or "An unknown error occurred.",
+        locations=(
+            [location.formatted for location in error.locations]
+            if error.locations is not None
+            else None
+        ),
+        path=error.path,
+    )
+    if error.extensions:
+        formatted.update(extensions=error.extensions)
+    return formatted
diff --git a/src/graphql/error/located_error.py b/src/graphql/error/located_error.py
new file mode 100644
index 0000000..8f40703
--- /dev/null
+++ b/src/graphql/error/located_error.py
@@ -0,0 +1,48 @@
+from typing import TYPE_CHECKING, Collection, Optional, Union
+
+from .graphql_error import GraphQLError
+
+if TYPE_CHECKING:
+    from ..language.ast import Node  # noqa: F401
+
+__all__ = ["located_error"]
+
+
+def located_error(
+    original_error: Union[Exception, GraphQLError],
+    nodes: Optional[Union["None", Collection["Node"]]],
+    path: Optional[Collection[Union[str, int]]] = None,
+) -> GraphQLError:
+    """Located GraphQL Error
+
+    Given an arbitrary Exception, presumably thrown while attempting to execute a
+    GraphQL operation, produce a new GraphQLError aware of the location in the document
+    responsible for the original Exception.
+    """
+    if not isinstance(original_error, Exception):
+        raise TypeError("Expected an Exception.")
+    # Note: this uses a brand-check to support GraphQL errors originating from
+    # other contexts.
+    if isinstance(original_error, GraphQLError) and original_error.path is not None:
+        return original_error
+    try:
+        # noinspection PyUnresolvedReferences
+        message = original_error.message  # type: ignore
+    except AttributeError:
+        message = str(original_error)
+    try:
+        # noinspection PyUnresolvedReferences
+        source = original_error.source  # type: ignore
+    except AttributeError:
+        source = None
+    try:
+        # noinspection PyUnresolvedReferences
+        positions = original_error.positions  # type: ignore
+    except AttributeError:
+        positions = None
+    try:
+        # noinspection PyUnresolvedReferences
+        nodes = original_error.nodes or nodes  # type: ignore
+    except AttributeError:
+        pass
+    return GraphQLError(message, nodes, source, positions, path, original_error)
diff --git a/src/graphql/error/syntax_error.py b/src/graphql/error/syntax_error.py
new file mode 100644
index 0000000..9ab41c2
--- /dev/null
+++ b/src/graphql/error/syntax_error.py
@@ -0,0 +1,18 @@
+from typing import TYPE_CHECKING
+
+from .graphql_error import GraphQLError
+
+if TYPE_CHECKING:
+    from ..language.source import Source  # noqa: F401
+
+__all__ = ["GraphQLSyntaxError"]
+
+
+class GraphQLSyntaxError(GraphQLError):
+    """A GraphQLError representing a syntax error."""
+
+    def __init__(self, source: "Source", position: int, description: str) -> None:
+        super().__init__(
+            f"Syntax Error: {description}", source=source, positions=[position]
+        )
+        self.description = description
diff --git a/src/graphql/execution/__init__.py b/src/graphql/execution/__init__.py
new file mode 100644
index 0000000..2b37b37
--- /dev/null
+++ b/src/graphql/execution/__init__.py
@@ -0,0 +1,29 @@
+"""GraphQL Execution
+
+The :mod:`graphql.execution` package is responsible for the execution phase of
+fulfilling a GraphQL request.
+"""
+
+from .execute import (
+    execute,
+    default_field_resolver,
+    default_type_resolver,
+    ExecutionContext,
+    ExecutionResult,
+    Middleware,
+)
+
+from .middleware import MiddlewareManager
+
+from .values import get_directive_values
+
+__all__ = [
+    "execute",
+    "default_field_resolver",
+    "default_type_resolver",
+    "ExecutionContext",
+    "ExecutionResult",
+    "Middleware",
+    "MiddlewareManager",
+    "get_directive_values",
+]
diff --git a/src/graphql/execution/execute.py b/src/graphql/execution/execute.py
new file mode 100644
index 0000000..c023b85
--- /dev/null
+++ b/src/graphql/execution/execute.py
@@ -0,0 +1,1201 @@
+from asyncio import gather
+from typing import (
+    Any,
+    Awaitable,
+    Callable,
+    Dict,
+    Iterable,
+    List,
+    NamedTuple,
+    Optional,
+    Set,
+    Union,
+    Tuple,
+    Type,
+    cast,
+)
+
+from ..error import GraphQLError, located_error
+from ..language import (
+    DocumentNode,
+    FieldNode,
+    FragmentDefinitionNode,
+    FragmentSpreadNode,
+    InlineFragmentNode,
+    OperationDefinitionNode,
+    OperationType,
+    SelectionSetNode,
+)
+from ..pyutils import (
+    inspect,
+    is_awaitable as default_is_awaitable,
+    AwaitableOrValue,
+    FrozenList,
+    Path,
+    Undefined,
+)
+from ..utilities.get_operation_root_type import get_operation_root_type
+from ..utilities.type_from_ast import type_from_ast
+from ..type import (
+    GraphQLAbstractType,
+    GraphQLField,
+    GraphQLIncludeDirective,
+    GraphQLLeafType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLOutputType,
+    GraphQLSchema,
+    GraphQLSkipDirective,
+    GraphQLFieldResolver,
+    GraphQLTypeResolver,
+    GraphQLResolveInfo,
+    SchemaMetaFieldDef,
+    TypeMetaFieldDef,
+    TypeNameMetaFieldDef,
+    assert_valid_schema,
+    is_abstract_type,
+    is_leaf_type,
+    is_list_type,
+    is_non_null_type,
+    is_object_type,
+)
+from .middleware import MiddlewareManager
+from .values import get_argument_values, get_directive_values, get_variable_values
+
+__all__ = [
+    "assert_valid_execution_arguments",
+    "default_field_resolver",
+    "default_type_resolver",
+    "execute",
+    "get_field_def",
+    "ExecutionResult",
+    "ExecutionContext",
+    "Middleware",
+]
+
+
+# Terminology
+#
+# "Definitions" are the generic name for top-level statements in the document.
+# Examples of this include:
+# 1) Operations (such as a query)
+# 2) Fragments
+#
+# "Operations" are a generic name for requests in the document.
+# Examples of this include:
+# 1) query,
+# 2) mutation
+#
+# "Selections" are the definitions that can appear legally and at
+# single level of the query. These include:
+# 1) field references e.g "a"
+# 2) fragment "spreads" e.g. "...c"
+# 3) inline fragment "spreads" e.g. "...on Type { a }"
+
+
+class ExecutionResult(NamedTuple):
+    """The result of GraphQL execution.
+
+    - ``data`` is the result of a successful execution of the query.
+    - ``errors`` is included when any errors occurred as a non-empty list.
+    """
+
+    data: Optional[Dict[str, Any]]
+    errors: Optional[List[GraphQLError]]
+
+
+# noinspection PyTypeHints
+ExecutionResult.__new__.__defaults__ = (None, None)  # type: ignore
+
+Middleware = Optional[Union[Tuple, List, MiddlewareManager]]
+
+
+class ExecutionContext:
+    """Data that must be available at all points during query execution.
+
+    Namely, schema of the type system that is currently executing, and the fragments
+    defined in the query document.
+    """
+
+    schema: GraphQLSchema
+    fragments: Dict[str, FragmentDefinitionNode]
+    root_value: Any
+    context_value: Any
+    operation: OperationDefinitionNode
+    variable_values: Dict[str, Any]
+    field_resolver: GraphQLFieldResolver
+    type_resolver: GraphQLTypeResolver
+    errors: List[GraphQLError]
+    middleware_manager: Optional[MiddlewareManager]
+
+    is_awaitable = staticmethod(default_is_awaitable)
+
+    def __init__(
+        self,
+        schema: GraphQLSchema,
+        fragments: Dict[str, FragmentDefinitionNode],
+        root_value: Any,
+        context_value: Any,
+        operation: OperationDefinitionNode,
+        variable_values: Dict[str, Any],
+        field_resolver: GraphQLFieldResolver,
+        type_resolver: GraphQLTypeResolver,
+        errors: List[GraphQLError],
+        middleware_manager: Optional[MiddlewareManager],
+        is_awaitable: Optional[Callable[[Any], bool]],
+    ) -> None:
+        self.schema = schema
+        self.fragments = fragments
+        self.root_value = root_value
+        self.context_value = context_value
+        self.operation = operation
+        self.variable_values = variable_values
+        self.field_resolver = field_resolver  # type: ignore
+        self.type_resolver = type_resolver  # type: ignore
+        self.errors = errors
+        self.middleware_manager = middleware_manager
+        if is_awaitable:
+            self.is_awaitable = is_awaitable
+        self._subfields_cache: Dict[Tuple, Dict[str, List[FieldNode]]] = {}
+
+    @classmethod
+    def build(
+        cls,
+        schema: GraphQLSchema,
+        document: DocumentNode,
+        root_value: Any = None,
+        context_value: Any = None,
+        raw_variable_values: Optional[Dict[str, Any]] = None,
+        operation_name: Optional[str] = None,
+        field_resolver: Optional[GraphQLFieldResolver] = None,
+        type_resolver: Optional[GraphQLTypeResolver] = None,
+        middleware: Optional[Middleware] = None,
+        is_awaitable: Optional[Callable[[Any], bool]] = None,
+    ) -> Union[List[GraphQLError], "ExecutionContext"]:
+        """Build an execution context
+
+        Constructs a ExecutionContext object from the arguments passed to execute, which
+        we will pass throughout the other execution methods.
+
+        Throws a GraphQLError if a valid execution context cannot be created.
+
+        For internal use only.
+        """
+        operation: Optional[OperationDefinitionNode] = None
+        fragments: Dict[str, FragmentDefinitionNode] = {}
+        middleware_manager: Optional[MiddlewareManager] = None
+        if middleware is not None:
+            if isinstance(middleware, (list, tuple)):
+                middleware_manager = MiddlewareManager(*middleware)
+            elif isinstance(middleware, MiddlewareManager):
+                middleware_manager = middleware
+            else:
+                raise TypeError(
+                    "Middleware must be passed as a list or tuple of functions"
+                    " or objects, or as a single MiddlewareManager object."
+                    f" Got {inspect(middleware)} instead."
+                )
+
+        for definition in document.definitions:
+            if isinstance(definition, OperationDefinitionNode):
+                if operation_name is None:
+                    if operation:
+                        return [
+                            GraphQLError(
+                                "Must provide operation name"
+                                " if query contains multiple operations."
+                            )
+                        ]
+                    operation = definition
+                elif definition.name and definition.name.value == operation_name:
+                    operation = definition
+            elif isinstance(definition, FragmentDefinitionNode):
+                fragments[definition.name.value] = definition
+
+        if not operation:
+            if operation_name is not None:
+                return [GraphQLError(f"Unknown operation named '{operation_name}'.")]
+            return [GraphQLError("Must provide an operation.")]
+
+        coerced_variable_values = get_variable_values(
+            schema,
+            operation.variable_definitions or FrozenList(),
+            raw_variable_values or {},
+            max_errors=50,
+        )
+
+        if isinstance(coerced_variable_values, list):
+            return coerced_variable_values  # errors
+
+        return cls(
+            schema,
+            fragments,
+            root_value,
+            context_value,
+            operation,
+            coerced_variable_values,  # coerced values
+            field_resolver or default_field_resolver,
+            type_resolver or default_type_resolver,
+            [],
+            middleware_manager,
+            is_awaitable,
+        )
+
+    def build_response(
+        self, data: AwaitableOrValue[Optional[Dict[str, Any]]]
+    ) -> AwaitableOrValue[ExecutionResult]:
+        """Build response.
+
+        Given a completed execution context and data, build the (data, errors) response
+        defined by the "Response" section of the GraphQL spec.
+        """
+        if self.is_awaitable(data):
+
+            async def build_response_async() -> ExecutionResult:
+                return self.build_response(await data)  # type: ignore
+
+            return build_response_async()
+        data = cast(Optional[Dict[str, Any]], data)
+        errors = self.errors
+        if not errors:
+            return ExecutionResult(data, None)
+        # Sort the error list in order to make it deterministic, since we might have
+        # been using parallel execution.
+        errors.sort(
+            key=lambda error: (error.locations or [], error.path or [], error.message)
+        )
+        return ExecutionResult(data, errors)
+
+    def execute_operation(
+        self, operation: OperationDefinitionNode, root_value: Any
+    ) -> Optional[AwaitableOrValue[Any]]:
+        """Execute an operation.
+
+        Implements the "Evaluating operations" section of the spec.
+        """
+        type_ = get_operation_root_type(self.schema, operation)
+        fields = self.collect_fields(type_, operation.selection_set, {}, set())
+
+        path = None
+
+        # Errors from sub-fields of a NonNull type may propagate to the top level, at
+        # which point we still log the error and null the parent field, which in this
+        # case is the entire response.
+        #
+        # Similar to complete_value_catching_error.
+        try:
+            # noinspection PyArgumentList
+            result = (
+                self.execute_fields_serially
+                if operation.operation == OperationType.MUTATION
+                else self.execute_fields
+            )(type_, root_value, path, fields)
+        except GraphQLError as error:
+            self.errors.append(error)
+            return None
+        else:
+            if self.is_awaitable(result):
+                # noinspection PyShadowingNames
+                async def await_result() -> Any:
+                    try:
+                        return await result  # type: ignore
+                    except GraphQLError as error:
+                        self.errors.append(error)
+
+                return await_result()
+            return result
+
+    def execute_fields_serially(
+        self,
+        parent_type: GraphQLObjectType,
+        source_value: Any,
+        path: Optional[Path],
+        fields: Dict[str, List[FieldNode]],
+    ) -> AwaitableOrValue[Dict[str, Any]]:
+        """Execute the given fields serially.
+
+        Implements the "Evaluating selection sets" section of the spec for "write" mode.
+        """
+        results: AwaitableOrValue[Dict[str, Any]] = {}
+        is_awaitable = self.is_awaitable
+        for response_name, field_nodes in fields.items():
+            field_path = Path(path, response_name)
+            result = self.resolve_field(
+                parent_type, source_value, field_nodes, field_path
+            )
+            if result is Undefined:
+                continue
+            if is_awaitable(results):
+                # noinspection PyShadowingNames
+                async def await_and_set_result(
+                    results: Awaitable[Dict[str, Any]],
+                    response_name: str,
+                    result: AwaitableOrValue[Any],
+                ) -> Dict[str, Any]:
+                    awaited_results = await results
+                    awaited_results[response_name] = (
+                        await result if is_awaitable(result) else result
+                    )
+                    return awaited_results
+
+                results = await_and_set_result(
+                    cast(Awaitable, results), response_name, result
+                )
+            elif is_awaitable(result):
+                # noinspection PyShadowingNames
+                async def set_result(
+                    results: Dict[str, Any], response_name: str, result: Awaitable,
+                ) -> Dict[str, Any]:
+                    results[response_name] = await result
+                    return results
+
+                results = set_result(
+                    cast(Dict[str, Any], results), response_name, result
+                )
+            else:
+                cast(Dict[str, Any], results)[response_name] = result
+        if is_awaitable(results):
+            # noinspection PyShadowingNames
+            async def get_results() -> Any:
+                return await cast(Awaitable, results)
+
+            return get_results()
+        return results
+
+    def execute_fields(
+        self,
+        parent_type: GraphQLObjectType,
+        source_value: Any,
+        path: Optional[Path],
+        fields: Dict[str, List[FieldNode]],
+    ) -> AwaitableOrValue[Dict[str, Any]]:
+        """Execute the given fields concurrently.
+
+        Implements the "Evaluating selection sets" section of the spec for "read" mode.
+        """
+        results = {}
+        is_awaitable = self.is_awaitable
+        awaitable_fields: List[str] = []
+        append_awaitable = awaitable_fields.append
+        for response_name, field_nodes in fields.items():
+            field_path = Path(path, response_name)
+            result = self.resolve_field(
+                parent_type, source_value, field_nodes, field_path
+            )
+            if result is not Undefined:
+                results[response_name] = result
+                if is_awaitable(result):
+                    append_awaitable(response_name)
+
+        #  If there are no coroutines, we can just return the object
+        if not awaitable_fields:
+            return results
+
+        # Otherwise, results is a map from field name to the result of resolving that
+        # field, which is possibly a coroutine object. Return a coroutine object that
+        # will yield this same map, but with any coroutines awaited in parallel and
+        # replaced with the values they yielded.
+        async def get_results() -> Dict[str, Any]:
+            results.update(
+                zip(
+                    awaitable_fields,
+                    await gather(*(results[field] for field in awaitable_fields)),
+                )
+            )
+            return results
+
+        return get_results()
+
+    def collect_fields(
+        self,
+        runtime_type: GraphQLObjectType,
+        selection_set: SelectionSetNode,
+        fields: Dict[str, List[FieldNode]],
+        visited_fragment_names: Set[str],
+    ) -> Dict[str, List[FieldNode]]:
+        """Collect fields.
+
+        Given a selection_set, adds all of the fields in that selection to the passed in
+        map of fields, and returns it at the end.
+
+        collect_fields requires the "runtime type" of an object. For a field which
+        returns an Interface or Union type, the "runtime type" will be the actual
+        Object type returned by that field.
+
+        For internal use only.
+        """
+        for selection in selection_set.selections:
+            if isinstance(selection, FieldNode):
+                if not self.should_include_node(selection):
+                    continue
+                name = get_field_entry_key(selection)
+                fields.setdefault(name, []).append(selection)
+            elif isinstance(selection, InlineFragmentNode):
+                if not self.should_include_node(
+                    selection
+                ) or not self.does_fragment_condition_match(selection, runtime_type):
+                    continue
+                self.collect_fields(
+                    runtime_type,
+                    selection.selection_set,
+                    fields,
+                    visited_fragment_names,
+                )
+            elif isinstance(selection, FragmentSpreadNode):  # pragma: no cover else
+                frag_name = selection.name.value
+                if frag_name in visited_fragment_names or not self.should_include_node(
+                    selection
+                ):
+                    continue
+                visited_fragment_names.add(frag_name)
+                fragment = self.fragments.get(frag_name)
+                if not fragment or not self.does_fragment_condition_match(
+                    fragment, runtime_type
+                ):
+                    continue
+                self.collect_fields(
+                    runtime_type, fragment.selection_set, fields, visited_fragment_names
+                )
+        return fields
+
+    def should_include_node(
+        self, node: Union[FragmentSpreadNode, FieldNode, InlineFragmentNode]
+    ) -> bool:
+        """Check if node should be included
+
+        Determines if a field should be included based on the @include and @skip
+        directives, where @skip has higher precedence than @include.
+        """
+        skip = get_directive_values(GraphQLSkipDirective, node, self.variable_values)
+        if skip and skip["if"]:
+            return False
+
+        include = get_directive_values(
+            GraphQLIncludeDirective, node, self.variable_values
+        )
+        if include and not include["if"]:
+            return False
+
+        return True
+
+    def does_fragment_condition_match(
+        self,
+        fragment: Union[FragmentDefinitionNode, InlineFragmentNode],
+        type_: GraphQLObjectType,
+    ) -> bool:
+        """Determine if a fragment is applicable to the given type."""
+        type_condition_node = fragment.type_condition
+        if not type_condition_node:
+            return True
+        conditional_type = type_from_ast(self.schema, type_condition_node)
+        if conditional_type is type_:
+            return True
+        if is_abstract_type(conditional_type):
+            return self.schema.is_sub_type(
+                cast(GraphQLAbstractType, conditional_type), type_
+            )
+        return False
+
+    def build_resolve_info(
+        self,
+        field_def: GraphQLField,
+        field_nodes: List[FieldNode],
+        parent_type: GraphQLObjectType,
+        path: Path,
+    ) -> GraphQLResolveInfo:
+        """Build the GraphQLResolveInfo object.
+
+        For internal use only."""
+        # The resolve function's first argument is a collection of information about
+        # the current execution state.
+        return GraphQLResolveInfo(
+            field_nodes[0].name.value,
+            field_nodes,
+            field_def.type,
+            parent_type,
+            path,
+            self.schema,
+            self.fragments,
+            self.root_value,
+            self.operation,
+            self.variable_values,
+            self.context_value,
+            self.is_awaitable,
+        )
+
+    def resolve_field(
+        self,
+        parent_type: GraphQLObjectType,
+        source: Any,
+        field_nodes: List[FieldNode],
+        path: Path,
+    ) -> AwaitableOrValue[Any]:
+        """Resolve the field on the given source object.
+
+        In particular, this figures out the value that the field returns by calling its
+        resolve function, then calls complete_value to await coroutine objects,
+        serialize scalars, or execute the sub-selection-set for objects.
+        """
+        field_node = field_nodes[0]
+        field_name = field_node.name.value
+
+        field_def = get_field_def(self.schema, parent_type, field_name)
+        if not field_def:
+            return Undefined
+
+        resolve_fn = field_def.resolve or self.field_resolver
+
+        if self.middleware_manager:
+            resolve_fn = self.middleware_manager.get_field_resolver(resolve_fn)
+
+        info = self.build_resolve_info(field_def, field_nodes, parent_type, path)
+
+        # Get the resolve function, regardless of if its result is normal or abrupt
+        # (error).
+        result = self.resolve_field_value_or_error(
+            field_def, field_nodes, resolve_fn, source, info
+        )
+
+        return self.complete_value_catching_error(
+            field_def.type, field_nodes, info, path, result
+        )
+
+    def resolve_field_value_or_error(
+        self,
+        field_def: GraphQLField,
+        field_nodes: List[FieldNode],
+        resolve_fn: GraphQLFieldResolver,
+        source: Any,
+        info: GraphQLResolveInfo,
+    ) -> Union[Exception, Any]:
+        """Resolve field to a value or an error.
+
+        Isolates the "ReturnOrAbrupt" behavior to not de-opt the resolve_field()
+        method. Returns the result of resolveFn or the abrupt-return Error object.
+
+        For internal use only.
+        """
+        try:
+            # Build a dictionary of arguments from the field.arguments AST, using the
+            # variables scope to fulfill any variable references.
+            args = get_argument_values(field_def, field_nodes[0], self.variable_values)
+
+            # Note that contrary to the JavaScript implementation, we pass the context
+            # value as part of the resolve info.
+            result = resolve_fn(source, info, **args)
+            if self.is_awaitable(result):
+                # noinspection PyShadowingNames
+                async def await_result() -> Any:
+                    try:
+                        return await result
+                    except GraphQLError as error:
+                        return error
+                    except Exception as error:
+                        return GraphQLError(str(error), original_error=error)
+
+                return await_result()
+            return result
+        except GraphQLError as error:
+            return error
+        except Exception as error:
+            return GraphQLError(str(error), original_error=error)
+
+    def complete_value_catching_error(
+        self,
+        return_type: GraphQLOutputType,
+        field_nodes: List[FieldNode],
+        info: GraphQLResolveInfo,
+        path: Path,
+        result: Any,
+    ) -> AwaitableOrValue[Any]:
+        """Complete a value while catching an error.
+
+        This is a small wrapper around completeValue which detects and logs errors in
+        the execution context.
+        """
+        completed: AwaitableOrValue[Any]
+        try:
+            if self.is_awaitable(result):
+
+                async def await_result() -> Any:
+                    value = self.complete_value(
+                        return_type, field_nodes, info, path, await result
+                    )
+                    if self.is_awaitable(value):
+                        return await value
+                    return value
+
+                completed = await_result()
+            else:
+                completed = self.complete_value(
+                    return_type, field_nodes, info, path, result
+                )
+            if self.is_awaitable(completed):
+                # noinspection PyShadowingNames
+                async def await_completed() -> Any:
+                    try:
+                        return await completed
+                    except Exception as error:
+                        self.handle_field_error(error, field_nodes, path, return_type)
+
+                return await_completed()
+            return completed
+        except Exception as error:
+            self.handle_field_error(error, field_nodes, path, return_type)
+            return None
+
+    def handle_field_error(
+        self,
+        raw_error: Exception,
+        field_nodes: List[FieldNode],
+        path: Path,
+        return_type: GraphQLOutputType,
+    ) -> None:
+        error = located_error(raw_error, field_nodes, path.as_list())
+
+        # If the field type is non-nullable, then it is resolved without any protection
+        # from errors, however it still properly locates the error.
+        if is_non_null_type(return_type):
+            raise error
+        # Otherwise, error protection is applied, logging the error and resolving a
+        # null value for this field if one is encountered.
+        self.errors.append(error)
+        return None
+
+    def complete_value(
+        self,
+        return_type: GraphQLOutputType,
+        field_nodes: List[FieldNode],
+        info: GraphQLResolveInfo,
+        path: Path,
+        result: Any,
+    ) -> AwaitableOrValue[Any]:
+        """Complete a value.
+
+        Implements the instructions for completeValue as defined in the "Field entries"
+        section of the spec.
+
+        If the field type is Non-Null, then this recursively completes the value for the
+        inner type. It throws a field error if that completion returns null, as per the
+        "Nullability" section of the spec.
+
+        If the field type is a List, then this recursively completes the value for the
+        inner type on each item in the list.
+
+        If the field type is a Scalar or Enum, ensures the completed value is a legal
+        value of the type by calling the ``serialize`` method of GraphQL type
+        definition.
+
+        If the field is an abstract type, determine the runtime type of the value and
+        then complete based on that type.
+
+        Otherwise, the field type expects a sub-selection set, and will complete the
+        value by evaluating all sub-selections.
+        """
+        # If result is an Exception, throw a located error.
+        if isinstance(result, Exception):
+            raise result
+
+        # If field type is NonNull, complete for inner type, and throw field error if
+        # result is null.
+        if is_non_null_type(return_type):
+            completed = self.complete_value(
+                cast(GraphQLNonNull, return_type).of_type,
+                field_nodes,
+                info,
+                path,
+                result,
+            )
+            if completed is None:
+                raise TypeError(
+                    "Cannot return null for non-nullable field"
+                    f" {info.parent_type.name}.{info.field_name}."
+                )
+            return completed
+
+        # If result value is null or undefined then return null.
+        if result is None or result is Undefined:
+            return None
+
+        # If field type is List, complete each item in the list with inner type
+        if is_list_type(return_type):
+            return self.complete_list_value(
+                cast(GraphQLList, return_type), field_nodes, info, path, result
+            )
+
+        # If field type is a leaf type, Scalar or Enum, serialize to a valid value,
+        # returning null if serialization is not possible.
+        if is_leaf_type(return_type):
+            return self.complete_leaf_value(cast(GraphQLLeafType, return_type), result)
+
+        # If field type is an abstract type, Interface or Union, determine the runtime
+        # Object type and complete for that type.
+        if is_abstract_type(return_type):
+            return self.complete_abstract_value(
+                cast(GraphQLAbstractType, return_type), field_nodes, info, path, result
+            )
+
+        # If field type is Object, execute and complete all sub-selections.
+        if is_object_type(return_type):
+            return self.complete_object_value(
+                cast(GraphQLObjectType, return_type), field_nodes, info, path, result
+            )
+
+        # Not reachable. All possible output types have been considered.
+        raise TypeError(  # pragma: no cover
+            "Cannot complete value of unexpected output type:"
+            f" '{inspect(return_type)}'."
+        )
+
+    def complete_list_value(
+        self,
+        return_type: GraphQLList[GraphQLOutputType],
+        field_nodes: List[FieldNode],
+        info: GraphQLResolveInfo,
+        path: Path,
+        result: Iterable[Any],
+    ) -> AwaitableOrValue[Any]:
+        """Complete a list value.
+
+        Complete a list value by completing each item in the list with the inner type.
+        """
+        if not isinstance(result, Iterable) or isinstance(result, str):
+            raise GraphQLError(
+                "Expected Iterable, but did not find one for field"
+                f" '{info.parent_type.name}.{info.field_name}'."
+            )
+
+        # This is specified as a simple map, however we're optimizing the path where
+        # the list contains no coroutine objects by avoiding creating another coroutine
+        # object.
+        item_type = return_type.of_type
+        is_awaitable = self.is_awaitable
+        awaitable_indices: List[int] = []
+        append_awaitable = awaitable_indices.append
+        completed_results: List[Any] = []
+        append_result = completed_results.append
+        for index, item in enumerate(result):
+            # No need to modify the info object containing the path, since from here on
+            # it is not ever accessed by resolver functions.
+            field_path = path.add_key(index)
+            completed_item = self.complete_value_catching_error(
+                item_type, field_nodes, info, field_path, item
+            )
+
+            if is_awaitable(completed_item):
+                append_awaitable(index)
+            append_result(completed_item)
+
+        if not awaitable_indices:
+            return completed_results
+
+        # noinspection PyShadowingNames
+        async def get_completed_results() -> Any:
+            for index, result in zip(
+                awaitable_indices,
+                await gather(
+                    *(completed_results[index] for index in awaitable_indices)
+                ),
+            ):
+                completed_results[index] = result
+            return completed_results
+
+        return get_completed_results()
+
+    @staticmethod
+    def complete_leaf_value(return_type: GraphQLLeafType, result: Any) -> Any:
+        """Complete a leaf value.
+
+        Complete a Scalar or Enum by serializing to a valid value, returning null if
+        serialization is not possible.
+        """
+        serialized_result = return_type.serialize(result)
+        if serialized_result is Undefined:
+            raise TypeError(
+                f"Expected a value of type '{inspect(return_type)}'"
+                f" but received: {inspect(result)}"
+            )
+        return serialized_result
+
+    def complete_abstract_value(
+        self,
+        return_type: GraphQLAbstractType,
+        field_nodes: List[FieldNode],
+        info: GraphQLResolveInfo,
+        path: Path,
+        result: Any,
+    ) -> AwaitableOrValue[Any]:
+        """Complete an abstract value.
+
+        Complete a value of an abstract type by determining the runtime object type of
+        that value, then complete the value for that type.
+        """
+        resolve_type_fn = return_type.resolve_type or self.type_resolver
+        runtime_type = resolve_type_fn(result, info, return_type)  # type: ignore
+
+        if self.is_awaitable(runtime_type):
+
+            async def await_complete_object_value() -> Any:
+                value = self.complete_object_value(
+                    self.ensure_valid_runtime_type(
+                        await runtime_type,  # type: ignore
+                        return_type,
+                        field_nodes,
+                        info,
+                        result,
+                    ),
+                    field_nodes,
+                    info,
+                    path,
+                    result,
+                )
+                if self.is_awaitable(value):
+                    return await value  # type: ignore
+                return value
+
+            return await_complete_object_value()
+        runtime_type = cast(Optional[Union[GraphQLObjectType, str]], runtime_type)
+
+        return self.complete_object_value(
+            self.ensure_valid_runtime_type(
+                runtime_type, return_type, field_nodes, info, result
+            ),
+            field_nodes,
+            info,
+            path,
+            result,
+        )
+
+    def ensure_valid_runtime_type(
+        self,
+        runtime_type_or_name: Optional[Union[GraphQLObjectType, str]],
+        return_type: GraphQLAbstractType,
+        field_nodes: List[FieldNode],
+        info: GraphQLResolveInfo,
+        result: Any,
+    ) -> GraphQLObjectType:
+        runtime_type = (
+            self.schema.get_type(runtime_type_or_name)
+            if isinstance(runtime_type_or_name, str)
+            else runtime_type_or_name
+        )
+
+        if not is_object_type(runtime_type):
+            raise GraphQLError(
+                f"Abstract type '{return_type.name}' must resolve"
+                " to an Object type at runtime"
+                f" for field '{info.parent_type.name}.{info.field_name}'"
+                f" with value {inspect(result)}, received '{inspect(runtime_type)}'."
+                f" Either the '{return_type.name}' type should provide"
+                " a 'resolve_type' function or each possible type should"
+                " provide an 'is_type_of' function.",
+                field_nodes,
+            )
+        runtime_type = cast(GraphQLObjectType, runtime_type)
+
+        if not self.schema.is_sub_type(return_type, runtime_type):
+            raise GraphQLError(
+                f"Runtime Object type '{runtime_type.name}' is not a possible"
+                f" type for '{return_type.name}'.",
+                field_nodes,
+            )
+
+        return runtime_type
+
+    def complete_object_value(
+        self,
+        return_type: GraphQLObjectType,
+        field_nodes: List[FieldNode],
+        info: GraphQLResolveInfo,
+        path: Path,
+        result: Any,
+    ) -> AwaitableOrValue[Dict[str, Any]]:
+        """Complete an Object value by executing all sub-selections."""
+        # If there is an `is_type_of()` predicate function, call it with the current
+        # result. If `is_type_of()` returns False, then raise an error rather than
+        #  continuing execution.
+        if return_type.is_type_of:
+            is_type_of = return_type.is_type_of(result, info)
+
+            if self.is_awaitable(is_type_of):
+
+                async def collect_and_execute_subfields_async() -> Dict[str, Any]:
+                    if not await is_type_of:  # type: ignore
+                        raise invalid_return_type_error(
+                            return_type, result, field_nodes
+                        )
+                    return self.collect_and_execute_subfields(
+                        return_type, field_nodes, path, result
+                    )  # type: ignore
+
+                return collect_and_execute_subfields_async()
+
+            if not is_type_of:
+                raise invalid_return_type_error(return_type, result, field_nodes)
+
+        return self.collect_and_execute_subfields(
+            return_type, field_nodes, path, result
+        )
+
+    def collect_and_execute_subfields(
+        self,
+        return_type: GraphQLObjectType,
+        field_nodes: List[FieldNode],
+        path: Path,
+        result: Any,
+    ) -> AwaitableOrValue[Dict[str, Any]]:
+        """Collect sub-fields to execute to complete this value."""
+        sub_field_nodes = self.collect_subfields(return_type, field_nodes)
+        return self.execute_fields(return_type, result, path, sub_field_nodes)
+
+    def collect_subfields(
+        self, return_type: GraphQLObjectType, field_nodes: List[FieldNode]
+    ) -> Dict[str, List[FieldNode]]:
+        """Collect subfields.
+
+        A cached collection of relevant subfields with regard to the return type is
+        kept in the execution context as ``_subfields_cache``. This ensures the
+        subfields are not repeatedly calculated, which saves overhead when resolving
+        lists of values.
+        """
+        cache = self._subfields_cache
+        # We cannot use the field_nodes themselves as key for the cache, since they
+        # are not hashable as a list. We also do not want to use the field_nodes
+        # themselves (converted to a tuple) as keys, since hashing them is slow.
+        # Therefore we use the ids of the field_nodes as keys. Note that we do not
+        # use the id of the list, since we want to hit the cache for all lists of
+        # the same nodes, not only for the same list of nodes. Also, the list id may
+        # even be reused, in which case we would get wrong results from the cache.
+        key = (
+            (return_type, id(field_nodes[0]))
+            if len(field_nodes) == 1  # optimize most frequent case
+            else tuple((return_type, *map(id, field_nodes)))
+        )
+        sub_field_nodes = cache.get(key)
+        if sub_field_nodes is None:
+            sub_field_nodes = {}
+            visited_fragment_names: Set[str] = set()
+            for field_node in field_nodes:
+                selection_set = field_node.selection_set
+                if selection_set:
+                    sub_field_nodes = self.collect_fields(
+                        return_type,
+                        selection_set,
+                        sub_field_nodes,
+                        visited_fragment_names,
+                    )
+            cache[key] = sub_field_nodes
+        return sub_field_nodes
+
+
+def execute(
+    schema: GraphQLSchema,
+    document: DocumentNode,
+    root_value: Any = None,
+    context_value: Any = None,
+    variable_values: Optional[Dict[str, Any]] = None,
+    operation_name: Optional[str] = None,
+    field_resolver: Optional[GraphQLFieldResolver] = None,
+    type_resolver: Optional[GraphQLTypeResolver] = None,
+    middleware: Optional[Middleware] = None,
+    execution_context_class: Optional[Type["ExecutionContext"]] = None,
+    is_awaitable: Optional[Callable[[Any], bool]] = None,
+) -> AwaitableOrValue[ExecutionResult]:
+    """Execute a GraphQL operation.
+
+    Implements the "Evaluating requests" section of the GraphQL specification.
+
+    Returns an ExecutionResult (if all encountered resolvers are synchronous),
+    or a coroutine object eventually yielding an ExecutionResult.
+
+    If the arguments to this function do not result in a legal execution context,
+    a GraphQLError will be thrown immediately explaining the invalid input.
+    """
+    # If arguments are missing or incorrect, throw an error.
+    assert_valid_execution_arguments(schema, document, variable_values)
+
+    if execution_context_class is None:
+        execution_context_class = ExecutionContext
+
+    # If a valid execution context cannot be created due to incorrect arguments,
+    # a "Response" with only errors is returned.
+    exe_context = execution_context_class.build(
+        schema,
+        document,
+        root_value,
+        context_value,
+        variable_values,
+        operation_name,
+        field_resolver,
+        type_resolver,
+        middleware,
+        is_awaitable,
+    )
+
+    # Return early errors if execution context failed.
+    if isinstance(exe_context, list):
+        return ExecutionResult(data=None, errors=exe_context)
+
+    # Return a possible coroutine object that will eventually yield the data described
+    # by the "Response" section of the GraphQL specification.
+    #
+    # If errors are encountered while executing a GraphQL field, only that field and
+    # its descendants will be omitted, and sibling fields will still be executed. An
+    # execution which encounters errors will still result in a coroutine object that
+    # can be executed without errors.
+
+    data = exe_context.execute_operation(exe_context.operation, root_value)
+    return exe_context.build_response(data)
+
+
+def assert_valid_execution_arguments(
+    schema: GraphQLSchema,
+    document: DocumentNode,
+    raw_variable_values: Optional[Dict[str, Any]] = None,
+) -> None:
+    """Check that the arguments are acceptable.
+
+    Essential assertions before executing to provide developer feedback for improper use
+    of the GraphQL library.
+
+    For internal use only.
+    """
+    if not document:
+        raise TypeError("Must provide document.")
+
+    # If the schema used for execution is invalid, throw an error.
+    assert_valid_schema(schema)
+
+    # Variables, if provided, must be a dictionary.
+    if not (raw_variable_values is None or isinstance(raw_variable_values, dict)):
+        raise TypeError(
+            "Variable values must be provided as a dictionary"
+            " with variable names as keys. Perhaps look to see"
+            " if an unparsed JSON string was provided."
+        )
+
+
+def get_field_def(
+    schema: GraphQLSchema, parent_type: GraphQLObjectType, field_name: str
+) -> GraphQLField:
+    """Get field definition.
+
+    This method looks up the field on the given type definition. It has special casing
+    for the three introspection fields, ``__schema``, ``__type`, and ``__typename``.
+    ``__typename`` is special because it can always be queried as a field, even in
+    situations where no other fields are allowed, like on a Union. ``__schema`` and
+    ``__type`` could get automatically added to the query type, but that would require
+    mutating type definitions, which would cause issues.
+
+    For internal use only.
+    """
+    if field_name == "__schema" and schema.query_type == parent_type:
+        return SchemaMetaFieldDef
+    elif field_name == "__type" and schema.query_type == parent_type:
+        return TypeMetaFieldDef
+    elif field_name == "__typename":
+        return TypeNameMetaFieldDef
+    return parent_type.fields.get(field_name)
+
+
+def get_field_entry_key(node: FieldNode) -> str:
+    """Implements the logic to compute the key of a given field's entry"""
+    return node.alias.value if node.alias else node.name.value
+
+
+def invalid_return_type_error(
+    return_type: GraphQLObjectType, result: Any, field_nodes: List[FieldNode]
+) -> GraphQLError:
+    """Create a GraphQLError for an invalid return type."""
+    return GraphQLError(
+        f"Expected value of type '{return_type.name}' but got: {inspect(result)}.",
+        field_nodes,
+    )
+
+
+def get_typename(value: Any) -> Optional[str]:
+    """Get the ``__typename`` property of the given value."""
+    if isinstance(value, dict):
+        return value.get("__typename")
+    # need to de-mangle the attribute assumed to be "private" in Python
+    for cls in value.__class__.__mro__:
+        __typename = getattr(value, f"_{cls.__name__}__typename", None)
+        if __typename:
+            return __typename
+    return None
+
+
+def default_type_resolver(
+    value: Any, info: GraphQLResolveInfo, abstract_type: GraphQLAbstractType
+) -> AwaitableOrValue[Optional[Union[GraphQLObjectType, str]]]:
+    """Default type resolver function.
+
+    If a resolve_type function is not given, then a default resolve behavior is used
+    which attempts two strategies:
+
+    First, See if the provided value has a ``__typename`` field defined, if so, use that
+    value as name of the resolved type.
+
+    Otherwise, test each possible type for the abstract type by calling
+    :meth:`~graphql.type.GraphQLObjectType.is_type_of` for the object
+    being coerced, returning the first type that matches.
+    """
+    # First, look for `__typename`.
+    type_name = get_typename(value)
+    if isinstance(type_name, str):
+        return type_name
+
+    # Otherwise, test each possible type.
+    possible_types = info.schema.get_possible_types(abstract_type)
+    is_awaitable = info.is_awaitable
+    awaitable_is_type_of_results: List[Awaitable] = []
+    append_awaitable_results = awaitable_is_type_of_results.append
+    awaitable_types: List[GraphQLObjectType] = []
+    append_awaitable_types = awaitable_types.append
+
+    for type_ in possible_types:
+        if type_.is_type_of:
+            is_type_of_result = type_.is_type_of(value, info)
+
+            if is_awaitable(is_type_of_result):
+                append_awaitable_results(cast(Awaitable, is_type_of_result))
+                append_awaitable_types(type_)
+            elif is_type_of_result:
+                return type_
+
+    if awaitable_is_type_of_results:
+        # noinspection PyShadowingNames
+        async def get_type() -> Optional[Union[GraphQLObjectType, str]]:
+            is_type_of_results = await gather(*awaitable_is_type_of_results)
+            for is_type_of_result, type_ in zip(is_type_of_results, awaitable_types):
+                if is_type_of_result:
+                    return type_
+            return None
+
+        return get_type()
+
+    return None
+
+
+def default_field_resolver(source: Any, info: GraphQLResolveInfo, **args: Any) -> Any:
+    """Default field resolver.
+
+    If a resolve function is not given, then a default resolve behavior is used which
+    takes the property of the source object of the same name as the field and returns
+    it as the result, or if it's a function, returns the result of calling that function
+    while passing along args and context.
+
+    For dictionaries, the field names are used as keys, for all other objects they are
+    used as attribute names.
+    """
+    # Ensure source is a value for which property access is acceptable.
+    field_name = info.field_name
+    value = (
+        source.get(field_name)
+        if isinstance(source, dict)
+        else getattr(source, field_name, None)
+    )
+    if callable(value):
+        return value(info, **args)
+    return value
diff --git a/src/graphql/execution/middleware.py b/src/graphql/execution/middleware.py
new file mode 100644
index 0000000..452b991
--- /dev/null
+++ b/src/graphql/execution/middleware.py
@@ -0,0 +1,63 @@
+from functools import partial, reduce
+from inspect import isfunction
+
+from typing import Callable, Iterator, Dict, List, Tuple, Any, Optional
+
+__all__ = ["MiddlewareManager"]
+
+GraphQLFieldResolver = Callable[..., Any]
+
+
+class MiddlewareManager:
+    """Manager for the middleware chain.
+
+    This class helps to wrap resolver functions with the provided middleware functions
+    and/or objects. The functions take the next middleware function as first argument.
+    If middleware is provided as an object, it must provide a method ``resolve`` that is
+    used as the middleware function.
+
+    Note that since resolvers return "AwaitableOrValue"s, all middleware functions
+    must be aware of this and check whether values are awaitable before awaiting them.
+    """
+
+    # allow custom attributes (not used internally)
+    __slots__ = "__dict__", "middlewares", "_middleware_resolvers", "_cached_resolvers"
+
+    _cached_resolvers: Dict[GraphQLFieldResolver, GraphQLFieldResolver]
+    _middleware_resolvers: Optional[List[Callable]]
+
+    def __init__(self, *middlewares: Any):
+        self.middlewares = middlewares
+        self._middleware_resolvers = (
+            list(get_middleware_resolvers(middlewares)) if middlewares else None
+        )
+        self._cached_resolvers = {}
+
+    def get_field_resolver(
+        self, field_resolver: GraphQLFieldResolver
+    ) -> GraphQLFieldResolver:
+        """Wrap the provided resolver with the middleware.
+
+        Returns a function that chains the middleware functions with the provided
+        resolver function.
+        """
+        if self._middleware_resolvers is None:
+            return field_resolver
+        if field_resolver not in self._cached_resolvers:
+            self._cached_resolvers[field_resolver] = reduce(
+                lambda chained_fns, next_fn: partial(next_fn, chained_fns),
+                self._middleware_resolvers,
+                field_resolver,
+            )
+        return self._cached_resolvers[field_resolver]
+
+
+def get_middleware_resolvers(middlewares: Tuple[Any, ...]) -> Iterator[Callable]:
+    """Get a list of resolver functions from a list of classes or functions."""
+    for middleware in middlewares:
+        if isfunction(middleware):
+            yield middleware
+        else:  # middleware provided as object with 'resolve' method
+            resolver_func = getattr(middleware, "resolve", None)
+            if resolver_func is not None:
+                yield resolver_func
diff --git a/src/graphql/execution/values.py b/src/graphql/execution/values.py
new file mode 100644
index 0000000..50e182d
--- /dev/null
+++ b/src/graphql/execution/values.py
@@ -0,0 +1,243 @@
+from typing import Any, Callable, Dict, List, Optional, Union, cast
+
+from ..error import GraphQLError
+from ..language import (
+    DirectiveNode,
+    ExecutableDefinitionNode,
+    FieldNode,
+    FieldDefinitionNode,
+    InputValueDefinitionNode,
+    NullValueNode,
+    SchemaDefinitionNode,
+    SelectionNode,
+    TypeDefinitionNode,
+    TypeExtensionNode,
+    VariableDefinitionNode,
+    VariableNode,
+    print_ast,
+)
+from ..pyutils import inspect, print_path_list, FrozenList, Undefined
+from ..type import (
+    GraphQLDirective,
+    GraphQLField,
+    GraphQLInputType,
+    GraphQLSchema,
+    is_input_type,
+    is_non_null_type,
+)
+from ..utilities.coerce_input_value import coerce_input_value
+from ..utilities.type_from_ast import type_from_ast
+from ..utilities.value_from_ast import value_from_ast
+
+__all__ = ["get_variable_values", "get_argument_values", "get_directive_values"]
+
+
+CoercedVariableValues = Union[List[GraphQLError], Dict[str, Any]]
+
+
+def get_variable_values(
+    schema: GraphQLSchema,
+    var_def_nodes: FrozenList[VariableDefinitionNode],
+    inputs: Dict[str, Any],
+    max_errors: Optional[int] = None,
+) -> CoercedVariableValues:
+    """Get coerced variable values based on provided definitions.
+
+    Prepares a dict of variable values of the correct type based on the provided
+    variable definitions and arbitrary input. If the input cannot be parsed to match
+    the variable definitions, a GraphQLError will be raised.
+
+    For internal use only.
+    """
+    errors: List[GraphQLError] = []
+
+    def on_error(error: GraphQLError) -> None:
+        if max_errors is not None and len(errors) >= max_errors:
+            raise GraphQLError(
+                "Too many errors processing variables,"
+                " error limit reached. Execution aborted."
+            )
+        errors.append(error)
+
+    try:
+        coerced = coerce_variable_values(schema, var_def_nodes, inputs, on_error)
+        if not errors:
+            return coerced
+    except GraphQLError as e:
+        errors.append(e)
+
+    return errors
+
+
+def coerce_variable_values(
+    schema: GraphQLSchema,
+    var_def_nodes: FrozenList[VariableDefinitionNode],
+    inputs: Dict[str, Any],
+    on_error: Callable[[GraphQLError], None],
+) -> Dict[str, Any]:
+    coerced_values: Dict[str, Any] = {}
+    for var_def_node in var_def_nodes:
+        var_name = var_def_node.variable.name.value
+        var_type = type_from_ast(schema, var_def_node.type)
+        if not is_input_type(var_type):
+            # Must use input types for variables. This should be caught during
+            # validation, however is checked again here for safety.
+            var_type_str = print_ast(var_def_node.type)
+            on_error(
+                GraphQLError(
+                    f"Variable '${var_name}' expected value of type '{var_type_str}'"
+                    " which cannot be used as an input type.",
+                    var_def_node.type,
+                )
+            )
+            continue
+
+        var_type = cast(GraphQLInputType, var_type)
+        if var_name not in inputs:
+            if var_def_node.default_value:
+                coerced_values[var_name] = value_from_ast(
+                    var_def_node.default_value, var_type
+                )
+            elif is_non_null_type(var_type):  # pragma: no cover else
+                var_type_str = inspect(var_type)
+                on_error(
+                    GraphQLError(
+                        f"Variable '${var_name}' of required type '{var_type_str}'"
+                        " was not provided.",
+                        var_def_node,
+                    )
+                )
+            continue
+
+        value = inputs[var_name]
+        if value is None and is_non_null_type(var_type):
+            var_type_str = inspect(var_type)
+            on_error(
+                GraphQLError(
+                    f"Variable '${var_name}' of non-null type '{var_type_str}'"
+                    " must not be null.",
+                    var_def_node,
+                )
+            )
+            continue
+
+        def on_input_value_error(
+            path: List[Union[str, int]], invalid_value: Any, error: GraphQLError
+        ) -> None:
+            invalid_str = inspect(invalid_value)
+            prefix = f"Variable '${var_name}' got invalid value {invalid_str}"
+            if path:
+                prefix += f" at '{var_name}{print_path_list(path)}'"
+            on_error(
+                GraphQLError(
+                    prefix + "; " + error.message,
+                    var_def_node,
+                    original_error=error.original_error,
+                )
+            )
+
+        coerced_values[var_name] = coerce_input_value(
+            value, var_type, on_input_value_error
+        )
+
+    return coerced_values
+
+
+def get_argument_values(
+    type_def: Union[GraphQLField, GraphQLDirective],
+    node: Union[FieldNode, DirectiveNode],
+    variable_values: Optional[Dict[str, Any]] = None,
+) -> Dict[str, Any]:
+    """Get coerced argument values based on provided definitions and nodes.
+
+    Prepares a dict of argument values given a list of argument definitions and list
+    of argument AST nodes.
+
+    For internal use only.
+    """
+    coerced_values: Dict[str, Any] = {}
+    arg_node_map = {arg.name.value: arg for arg in node.arguments or []}
+
+    for name, arg_def in type_def.args.items():
+        arg_type = arg_def.type
+        argument_node = arg_node_map.get(name)
+
+        if argument_node is None:
+            if arg_def.default_value is not Undefined:
+                coerced_values[arg_def.out_name or name] = arg_def.default_value
+            elif is_non_null_type(arg_type):  # pragma: no cover else
+                raise GraphQLError(
+                    f"Argument '{name}' of required type '{arg_type}'"
+                    " was not provided.",
+                    node,
+                )
+            continue  # pragma: no cover
+
+        value_node = argument_node.value
+        is_null = isinstance(argument_node.value, NullValueNode)
+
+        if isinstance(value_node, VariableNode):
+            variable_name = value_node.name.value
+            if variable_values is None or variable_name not in variable_values:
+                if arg_def.default_value is not Undefined:
+                    coerced_values[arg_def.out_name or name] = arg_def.default_value
+                elif is_non_null_type(arg_type):  # pragma: no cover else
+                    raise GraphQLError(
+                        f"Argument '{name}' of required type '{arg_type}'"
+                        f" was provided the variable '${variable_name}'"
+                        " which was not provided a runtime value.",
+                        value_node,
+                    )
+                continue  # pragma: no cover
+            is_null = variable_values[variable_name] is None
+
+        if is_null and is_non_null_type(arg_type):
+            raise GraphQLError(
+                f"Argument '{name}' of non-null type '{arg_type}' must not be null.",
+                value_node,
+            )
+
+        coerced_value = value_from_ast(value_node, arg_type, variable_values)
+        if coerced_value is Undefined:
+            # Note: `values_of_correct_type` validation should catch this before
+            # execution. This is a runtime check to ensure execution does not
+            # continue with an invalid argument value.
+            raise GraphQLError(
+                f"Argument '{name}' has invalid value {print_ast(value_node)}.",
+                value_node,
+            )
+        coerced_values[arg_def.out_name or name] = coerced_value
+
+    return coerced_values
+
+
+NodeWithDirective = Union[
+    ExecutableDefinitionNode,
+    FieldDefinitionNode,
+    InputValueDefinitionNode,
+    SelectionNode,
+    SchemaDefinitionNode,
+    TypeDefinitionNode,
+    TypeExtensionNode,
+]
+
+
+def get_directive_values(
+    directive_def: GraphQLDirective,
+    node: NodeWithDirective,
+    variable_values: Optional[Dict[str, Any]] = None,
+) -> Optional[Dict[str, Any]]:
+    """Get coerced argument values based on provided nodes.
+
+    Prepares a dict of argument values given a directive definition and an AST node
+    which may contain directives. Optionally also accepts a dict of variable values.
+
+    If the directive does not exist on the node, returns None.
+    """
+    directives = node.directives
+    if directives:
+        directive_name = directive_def.name
+        for directive in directives:
+            if directive.name.value == directive_name:
+                return get_argument_values(directive_def, directive, variable_values)
+    return None
diff --git a/src/graphql/graphql.py b/src/graphql/graphql.py
new file mode 100644
index 0000000..5b13a55
--- /dev/null
+++ b/src/graphql/graphql.py
@@ -0,0 +1,197 @@
+from asyncio import ensure_future
+from inspect import isawaitable
+from typing import Any, Awaitable, Callable, Dict, Optional, Union, Type, cast
+
+from .error import GraphQLError
+from .execution import execute, ExecutionResult, ExecutionContext, Middleware
+from .language import parse, Source
+from .pyutils import AwaitableOrValue
+from .type import (
+    GraphQLFieldResolver,
+    GraphQLSchema,
+    GraphQLTypeResolver,
+    validate_schema,
+)
+
+__all__ = ["graphql", "graphql_sync"]
+
+
+async def graphql(
+    schema: GraphQLSchema,
+    source: Union[str, Source],
+    root_value: Any = None,
+    context_value: Any = None,
+    variable_values: Optional[Dict[str, Any]] = None,
+    operation_name: Optional[str] = None,
+    field_resolver: Optional[GraphQLFieldResolver] = None,
+    type_resolver: Optional[GraphQLTypeResolver] = None,
+    middleware: Optional[Middleware] = None,
+    execution_context_class: Optional[Type[ExecutionContext]] = None,
+    is_awaitable: Optional[Callable[[Any], bool]] = None,
+) -> ExecutionResult:
+    """Execute a GraphQL operation asynchronously.
+
+    This is the primary entry point function for fulfilling GraphQL operations by
+    parsing, validating, and executing a GraphQL document along side a GraphQL schema.
+
+    More sophisticated GraphQL servers, such as those which persist queries, may wish
+    to separate the validation and execution phases to a static time tooling step,
+    and a server runtime step.
+
+    Accepts the following arguments:
+
+    :arg schema:
+      The GraphQL type system to use when validating and executing a query.
+    :arg source:
+      A GraphQL language formatted string representing the requested operation.
+    :arg root_value:
+      The value provided as the first argument to resolver functions on the top level
+      type (e.g. the query object type).
+    :arg context_value:
+      The context value is provided as an attribute of the second argument
+      (the resolve info) to resolver functions. It is used to pass shared information
+      useful at any point during query execution, for example the currently logged in
+      user and connections to databases or other services.
+    :arg variable_values:
+      A mapping of variable name to runtime value to use for all variables defined
+      in the request string.
+    :arg operation_name:
+      The name of the operation to use if request string contains multiple possible
+      operations. Can be omitted if request string contains only one operation.
+    :arg field_resolver:
+      A resolver function to use when one is not provided by the schema.
+      If not provided, the default field resolver is used (which looks for a value
+      or method on the source value with the field's name).
+    :arg type_resolver:
+      A type resolver function to use when none is provided by the schema.
+      If not provided, the default type resolver is used (which looks for a
+      ``__typename`` field or alternatively calls the
+      :meth:`~graphql.type.GraphQLObjectType.is_type_of` method).
+    :arg middleware:
+      The middleware to wrap the resolvers with
+    :arg execution_context_class:
+      The execution context class to use to build the context
+    :arg is_awaitable:
+      The predicate to be used for checking whether values are awaitable
+    """
+    # Always return asynchronously for a consistent API.
+    result = graphql_impl(
+        schema,
+        source,
+        root_value,
+        context_value,
+        variable_values,
+        operation_name,
+        field_resolver,
+        type_resolver,
+        middleware,
+        execution_context_class,
+        is_awaitable,
+    )
+
+    if isawaitable(result):
+        return await cast(Awaitable[ExecutionResult], result)
+
+    return cast(ExecutionResult, result)
+
+
+def assume_not_awaitable(_value: Any) -> bool:
+    """Replacement for isawaitable if everything is assumed to be synchronous."""
+    return False
+
+
+def graphql_sync(
+    schema: GraphQLSchema,
+    source: Union[str, Source],
+    root_value: Any = None,
+    context_value: Any = None,
+    variable_values: Optional[Dict[str, Any]] = None,
+    operation_name: Optional[str] = None,
+    field_resolver: Optional[GraphQLFieldResolver] = None,
+    type_resolver: Optional[GraphQLTypeResolver] = None,
+    middleware: Optional[Middleware] = None,
+    execution_context_class: Optional[Type[ExecutionContext]] = None,
+    check_sync: bool = False,
+) -> ExecutionResult:
+    """Execute a GraphQL operation synchronously.
+
+    The graphql_sync function also fulfills GraphQL operations by parsing, validating,
+    and executing a GraphQL document along side a GraphQL schema. However, it guarantees
+    to complete synchronously (or throw an error) assuming that all field resolvers
+    are also synchronous.
+
+    Set check_sync to True to still run checks that no awaitable values are returned.
+    """
+    is_awaitable = (
+        check_sync
+        if callable(check_sync)
+        else (None if check_sync else assume_not_awaitable)
+    )
+    result = graphql_impl(
+        schema,
+        source,
+        root_value,
+        context_value,
+        variable_values,
+        operation_name,
+        field_resolver,
+        type_resolver,
+        middleware,
+        execution_context_class,
+        is_awaitable,
+    )
+
+    # Assert that the execution was synchronous.
+    if isawaitable(result):
+        ensure_future(cast(Awaitable[ExecutionResult], result)).cancel()
+        raise RuntimeError("GraphQL execution failed to complete synchronously.")
+
+    return cast(ExecutionResult, result)
+
+
+def graphql_impl(
+    schema: GraphQLSchema,
+    source: Union[str, Source],
+    root_value: Any,
+    context_value: Any,
+    variable_values: Optional[Dict[str, Any]],
+    operation_name: Optional[str],
+    field_resolver: Optional[GraphQLFieldResolver],
+    type_resolver: Optional[GraphQLTypeResolver],
+    middleware: Optional[Middleware],
+    execution_context_class: Optional[Type[ExecutionContext]],
+    is_awaitable: Optional[Callable[[Any], bool]],
+) -> AwaitableOrValue[ExecutionResult]:
+    """Execute a query, return asynchronously only if necessary."""
+    # Validate Schema
+    schema_validation_errors = validate_schema(schema)
+    if schema_validation_errors:
+        return ExecutionResult(data=None, errors=schema_validation_errors)
+
+    # Parse
+    try:
+        document = parse(source)
+    except GraphQLError as error:
+        return ExecutionResult(data=None, errors=[error])
+
+    # Validate
+    from .validation import validate
+
+    validation_errors = validate(schema, document)
+    if validation_errors:
+        return ExecutionResult(data=None, errors=validation_errors)
+
+    # Execute
+    return execute(
+        schema,
+        document,
+        root_value,
+        context_value,
+        variable_values,
+        operation_name,
+        field_resolver,
+        type_resolver,
+        middleware,
+        execution_context_class,
+        is_awaitable,
+    )
diff --git a/src/graphql/language/__init__.py b/src/graphql/language/__init__.py
new file mode 100644
index 0000000..9e5904a
--- /dev/null
+++ b/src/graphql/language/__init__.py
@@ -0,0 +1,190 @@
+"""GraphQL Language
+
+The :mod:`graphql.language` package is responsible for parsing and operating on the
+GraphQL language.
+"""
+
+from .source import Source
+
+from .location import get_location, SourceLocation
+
+from .print_location import print_location, print_source_location
+
+from .token_kind import TokenKind
+
+from .lexer import Lexer
+
+from .parser import parse, parse_type, parse_value
+
+from .printer import print_ast
+
+from .visitor import (
+    visit,
+    Visitor,
+    ParallelVisitor,
+    VisitorAction,
+    BREAK,
+    SKIP,
+    REMOVE,
+    IDLE,
+)
+
+from .ast import (
+    Location,
+    Token,
+    Node,
+    # Each kind of AST node
+    NameNode,
+    DocumentNode,
+    DefinitionNode,
+    ExecutableDefinitionNode,
+    OperationDefinitionNode,
+    OperationType,
+    VariableDefinitionNode,
+    VariableNode,
+    SelectionSetNode,
+    SelectionNode,
+    FieldNode,
+    ArgumentNode,
+    FragmentSpreadNode,
+    InlineFragmentNode,
+    FragmentDefinitionNode,
+    ValueNode,
+    IntValueNode,
+    FloatValueNode,
+    StringValueNode,
+    BooleanValueNode,
+    NullValueNode,
+    EnumValueNode,
+    ListValueNode,
+    ObjectValueNode,
+    ObjectFieldNode,
+    DirectiveNode,
+    TypeNode,
+    NamedTypeNode,
+    ListTypeNode,
+    NonNullTypeNode,
+    TypeSystemDefinitionNode,
+    SchemaDefinitionNode,
+    OperationTypeDefinitionNode,
+    TypeDefinitionNode,
+    ScalarTypeDefinitionNode,
+    ObjectTypeDefinitionNode,
+    FieldDefinitionNode,
+    InputValueDefinitionNode,
+    InterfaceTypeDefinitionNode,
+    UnionTypeDefinitionNode,
+    EnumTypeDefinitionNode,
+    EnumValueDefinitionNode,
+    InputObjectTypeDefinitionNode,
+    DirectiveDefinitionNode,
+    TypeSystemExtensionNode,
+    SchemaExtensionNode,
+    TypeExtensionNode,
+    ScalarTypeExtensionNode,
+    ObjectTypeExtensionNode,
+    InterfaceTypeExtensionNode,
+    UnionTypeExtensionNode,
+    EnumTypeExtensionNode,
+    InputObjectTypeExtensionNode,
+)
+from .predicates import (
+    is_definition_node,
+    is_executable_definition_node,
+    is_selection_node,
+    is_value_node,
+    is_type_node,
+    is_type_system_definition_node,
+    is_type_definition_node,
+    is_type_system_extension_node,
+    is_type_extension_node,
+)
+from .directive_locations import DirectiveLocation
+
+__all__ = [
+    "get_location",
+    "SourceLocation",
+    "print_location",
+    "print_source_location",
+    "TokenKind",
+    "Lexer",
+    "parse",
+    "parse_value",
+    "parse_type",
+    "print_ast",
+    "Source",
+    "visit",
+    "Visitor",
+    "ParallelVisitor",
+    "VisitorAction",
+    "BREAK",
+    "SKIP",
+    "REMOVE",
+    "IDLE",
+    "Location",
+    "Token",
+    "DirectiveLocation",
+    "Node",
+    "NameNode",
+    "DocumentNode",
+    "DefinitionNode",
+    "ExecutableDefinitionNode",
+    "OperationDefinitionNode",
+    "OperationType",
+    "VariableDefinitionNode",
+    "VariableNode",
+    "SelectionSetNode",
+    "SelectionNode",
+    "FieldNode",
+    "ArgumentNode",
+    "FragmentSpreadNode",
+    "InlineFragmentNode",
+    "FragmentDefinitionNode",
+    "ValueNode",
+    "IntValueNode",
+    "FloatValueNode",
+    "StringValueNode",
+    "BooleanValueNode",
+    "NullValueNode",
+    "EnumValueNode",
+    "ListValueNode",
+    "ObjectValueNode",
+    "ObjectFieldNode",
+    "DirectiveNode",
+    "TypeNode",
+    "NamedTypeNode",
+    "ListTypeNode",
+    "NonNullTypeNode",
+    "TypeSystemDefinitionNode",
+    "SchemaDefinitionNode",
+    "OperationTypeDefinitionNode",
+    "TypeDefinitionNode",
+    "ScalarTypeDefinitionNode",
+    "ObjectTypeDefinitionNode",
+    "FieldDefinitionNode",
+    "InputValueDefinitionNode",
+    "InterfaceTypeDefinitionNode",
+    "UnionTypeDefinitionNode",
+    "EnumTypeDefinitionNode",
+    "EnumValueDefinitionNode",
+    "InputObjectTypeDefinitionNode",
+    "DirectiveDefinitionNode",
+    "TypeSystemExtensionNode",
+    "SchemaExtensionNode",
+    "TypeExtensionNode",
+    "ScalarTypeExtensionNode",
+    "ObjectTypeExtensionNode",
+    "InterfaceTypeExtensionNode",
+    "UnionTypeExtensionNode",
+    "EnumTypeExtensionNode",
+    "InputObjectTypeExtensionNode",
+    "is_definition_node",
+    "is_executable_definition_node",
+    "is_selection_node",
+    "is_value_node",
+    "is_type_node",
+    "is_type_system_definition_node",
+    "is_type_definition_node",
+    "is_type_system_extension_node",
+    "is_type_extension_node",
+]
diff --git a/src/graphql/language/ast.py b/src/graphql/language/ast.py
new file mode 100644
index 0000000..188cfe6
--- /dev/null
+++ b/src/graphql/language/ast.py
@@ -0,0 +1,641 @@
+from copy import copy, deepcopy
+from enum import Enum
+from typing import Any, Dict, List, Optional, Union
+
+from .source import Source
+from .token_kind import TokenKind
+from ..pyutils import camel_to_snake, FrozenList
+
+__all__ = [
+    "Location",
+    "Token",
+    "Node",
+    "NameNode",
+    "DocumentNode",
+    "DefinitionNode",
+    "ExecutableDefinitionNode",
+    "OperationDefinitionNode",
+    "VariableDefinitionNode",
+    "SelectionSetNode",
+    "SelectionNode",
+    "FieldNode",
+    "ArgumentNode",
+    "FragmentSpreadNode",
+    "InlineFragmentNode",
+    "FragmentDefinitionNode",
+    "ValueNode",
+    "VariableNode",
+    "IntValueNode",
+    "FloatValueNode",
+    "StringValueNode",
+    "BooleanValueNode",
+    "NullValueNode",
+    "EnumValueNode",
+    "ListValueNode",
+    "ObjectValueNode",
+    "ObjectFieldNode",
+    "DirectiveNode",
+    "TypeNode",
+    "NamedTypeNode",
+    "ListTypeNode",
+    "NonNullTypeNode",
+    "TypeSystemDefinitionNode",
+    "SchemaDefinitionNode",
+    "OperationType",
+    "OperationTypeDefinitionNode",
+    "TypeDefinitionNode",
+    "ScalarTypeDefinitionNode",
+    "ObjectTypeDefinitionNode",
+    "FieldDefinitionNode",
+    "InputValueDefinitionNode",
+    "InterfaceTypeDefinitionNode",
+    "UnionTypeDefinitionNode",
+    "EnumTypeDefinitionNode",
+    "EnumValueDefinitionNode",
+    "InputObjectTypeDefinitionNode",
+    "DirectiveDefinitionNode",
+    "SchemaExtensionNode",
+    "TypeExtensionNode",
+    "TypeSystemExtensionNode",
+    "ScalarTypeExtensionNode",
+    "ObjectTypeExtensionNode",
+    "InterfaceTypeExtensionNode",
+    "UnionTypeExtensionNode",
+    "EnumTypeExtensionNode",
+    "InputObjectTypeExtensionNode",
+]
+
+
+class Token:
+    """AST Token
+
+    Represents a range of characters represented by a lexical token within a Source.
+    """
+
+    __slots__ = "kind", "start", "end", "line", "column", "prev", "next", "value"
+
+    kind: TokenKind  # the kind of token
+    start: int  # the character offset at which this Node begins
+    end: int  # the character offset at which this Node ends
+    line: int  # the 1-indexed line number on which this Token appears
+    column: int  # the 1-indexed column number at which this Token begins
+    # for non-punctuation tokens, represents the interpreted value of the token:
+    value: Optional[str]
+    # Tokens exist as nodes in a double-linked-list amongst all tokens including
+    # ignored tokens. <SOF> is always the first node and <EOF> the last.
+    prev: Optional["Token"]
+    next: Optional["Token"]
+
+    def __init__(
+        self,
+        kind: TokenKind,
+        start: int,
+        end: int,
+        line: int,
+        column: int,
+        prev: Optional["Token"] = None,
+        value: Optional[str] = None,
+    ) -> None:
+        self.kind = kind
+        self.start, self.end = start, end
+        self.line, self.column = line, column
+        self.value = value
+        self.prev = prev
+        self.next = None
+
+    def __str__(self) -> str:
+        return self.desc
+
+    def __repr__(self) -> str:
+        """Print a simplified form when appearing in repr() or inspect()."""
+        return f"<Token {self.desc} {self.line}:{self.column}>"
+
+    def __inspect__(self) -> str:
+        return repr(self)
+
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, Token):
+            return (
+                self.kind == other.kind
+                and self.start == other.start
+                and self.end == other.end
+                and self.line == other.line
+                and self.column == other.column
+                and self.value == other.value
+            )
+        elif isinstance(other, str):
+            return other == self.desc
+        return False
+
+    def __hash__(self) -> int:
+        return hash(
+            (self.kind, self.start, self.end, self.line, self.column, self.value)
+        )
+
+    def __copy__(self) -> "Token":
+        """Create a shallow copy of the token"""
+        return self.__class__(
+            self.kind,
+            self.start,
+            self.end,
+            self.line,
+            self.column,
+            self.prev,
+            self.value,
+        )
+
+    def __deepcopy__(self, memo: Dict) -> "Token":
+        """Allow only shallow copies to avoid recursion."""
+        return copy(self)
+
+    @property
+    def desc(self) -> str:
+        """A helper property to describe a token as a string for debugging"""
+        kind, value = self.kind.value, self.value
+        return f"{kind} {value!r}" if value else kind
+
+
+class Location:
+    """AST Location
+
+    Contains a range of UTF-8 character offsets and token references that identify the
+    region of the source from which the AST derived.
+    """
+
+    __slots__ = (
+        "start",
+        "end",
+        "start_token",
+        "end_token",
+        "source",
+    )
+
+    start: int  # character offset at which this Node begins
+    end: int  # character offset at which this Node ends
+    start_token: Token  # Token at which this Node begins
+    end_token: Token  # Token at which this Node ends.
+    source: Source  # Source document the AST represents
+
+    def __init__(self, start_token: Token, end_token: Token, source: Source) -> None:
+        self.start = start_token.start
+        self.end = end_token.end
+        self.start_token = start_token
+        self.end_token = end_token
+        self.source = source
+
+    def __str__(self) -> str:
+        return f"{self.start}:{self.end}"
+
+    def __repr__(self) -> str:
+        """Print a simplified form when appearing in repr() or inspect()."""
+        return f"<Location {self.start}:{self.end}>"
+
+    def __inspect__(self) -> str:
+        return repr(self)
+
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, Location):
+            return self.start == other.start and self.end == other.end
+        elif isinstance(other, (list, tuple)) and len(other) == 2:
+            return self.start == other[0] and self.end == other[1]
+        return False
+
+    def __ne__(self, other: Any) -> bool:
+        return not self == other
+
+    def __hash__(self) -> int:
+        return hash((self.start, self.end))
+
+
+class OperationType(Enum):
+
+    QUERY = "query"
+    MUTATION = "mutation"
+    SUBSCRIPTION = "subscription"
+
+
+# Base AST Node
+
+
+class Node:
+    """AST nodes"""
+
+    # allow custom attributes and weak references (not used internally)
+    __slots__ = "__dict__", "__weakref__", "loc"
+
+    loc: Optional[Location]
+
+    kind: str = "ast"  # the kind of the node as a snake_case string
+    keys = ["loc"]  # the names of the attributes of this node
+
+    def __init__(self, **kwargs: Any) -> None:
+        """Initialize the node with the given keyword arguments."""
+        for key in self.keys:
+            value = kwargs.get(key)
+            if isinstance(value, list) and not isinstance(value, FrozenList):
+                value = FrozenList(value)
+            setattr(self, key, value)
+
+    def __repr__(self) -> str:
+        """Get a simple representation of the node."""
+        name, loc = self.__class__.__name__, getattr(self, "loc", None)
+        return f"{name} at {loc}" if loc else name
+
+    def __eq__(self, other: Any) -> bool:
+        """Test whether two nodes are equal (recursively)."""
+        return (
+            isinstance(other, Node)
+            and self.__class__ == other.__class__
+            and all(getattr(self, key) == getattr(other, key) for key in self.keys)
+        )
+
+    def __hash__(self) -> int:
+        return hash(tuple(getattr(self, key) for key in self.keys))
+
+    def __copy__(self) -> "Node":
+        """Create a shallow copy of the node."""
+        return self.__class__(**{key: getattr(self, key) for key in self.keys})
+
+    def __deepcopy__(self, memo: Dict) -> "Node":
+        """Create a deep copy of the node"""
+        # noinspection PyArgumentList
+        return self.__class__(
+            **{key: deepcopy(getattr(self, key), memo) for key in self.keys}
+        )
+
+    def __init_subclass__(cls) -> None:
+        super().__init_subclass__()
+        name = cls.__name__
+        if name.endswith("Node"):
+            name = name[:-4]
+        cls.kind = camel_to_snake(name)
+        keys: List[str] = []
+        for base in cls.__bases__:
+            # noinspection PyUnresolvedReferences
+            keys.extend(base.keys)  # type: ignore
+        keys.extend(cls.__slots__)
+        cls.keys = keys
+
+
+# Name
+
+
+class NameNode(Node):
+    __slots__ = ("value",)
+
+    value: str
+
+
+# Document
+
+
+class DocumentNode(Node):
+    __slots__ = ("definitions",)
+
+    definitions: FrozenList["DefinitionNode"]
+
+
+class DefinitionNode(Node):
+    __slots__ = ()
+
+
+class ExecutableDefinitionNode(DefinitionNode):
+    __slots__ = "name", "directives", "variable_definitions", "selection_set"
+
+    name: Optional[NameNode]
+    directives: FrozenList["DirectiveNode"]
+    variable_definitions: FrozenList["VariableDefinitionNode"]
+    selection_set: "SelectionSetNode"
+
+
+class OperationDefinitionNode(ExecutableDefinitionNode):
+    __slots__ = ("operation",)
+
+    operation: OperationType
+
+
+class VariableDefinitionNode(Node):
+    __slots__ = "variable", "type", "default_value", "directives"
+
+    variable: "VariableNode"
+    type: "TypeNode"
+    default_value: Optional["ValueNode"]
+    directives: FrozenList["DirectiveNode"]
+
+
+class SelectionSetNode(Node):
+    __slots__ = ("selections",)
+
+    selections: FrozenList["SelectionNode"]
+
+
+class SelectionNode(Node):
+    __slots__ = ("directives",)
+
+    directives: FrozenList["DirectiveNode"]
+
+
+class FieldNode(SelectionNode):
+    __slots__ = "alias", "name", "arguments", "selection_set"
+
+    alias: Optional[NameNode]
+    name: NameNode
+    arguments: FrozenList["ArgumentNode"]
+    selection_set: Optional[SelectionSetNode]
+
+
+class ArgumentNode(Node):
+    __slots__ = "name", "value"
+
+    name: NameNode
+    value: "ValueNode"
+
+
+# Fragments
+
+
+class FragmentSpreadNode(SelectionNode):
+    __slots__ = ("name",)
+
+    name: NameNode
+
+
+class InlineFragmentNode(SelectionNode):
+    __slots__ = "type_condition", "selection_set"
+
+    type_condition: "NamedTypeNode"
+    selection_set: SelectionSetNode
+
+
+class FragmentDefinitionNode(ExecutableDefinitionNode):
+    __slots__ = ("type_condition",)
+
+    name: NameNode
+    type_condition: "NamedTypeNode"
+
+
+# Values
+
+
+class ValueNode(Node):
+    __slots__ = ()
+
+
+class VariableNode(ValueNode):
+    __slots__ = ("name",)
+
+    name: NameNode
+
+
+class IntValueNode(ValueNode):
+    __slots__ = ("value",)
+
+    value: str
+
+
+class FloatValueNode(ValueNode):
+    __slots__ = ("value",)
+
+    value: str
+
+
+class StringValueNode(ValueNode):
+    __slots__ = "value", "block"
+
+    value: str
+    block: Optional[bool]
+
+
+class BooleanValueNode(ValueNode):
+    __slots__ = ("value",)
+
+    value: bool
+
+
+class NullValueNode(ValueNode):
+    __slots__ = ()
+
+
+class EnumValueNode(ValueNode):
+    __slots__ = ("value",)
+
+    value: str
+
+
+class ListValueNode(ValueNode):
+    __slots__ = ("values",)
+
+    values: FrozenList[ValueNode]
+
+
+class ObjectValueNode(ValueNode):
+    __slots__ = ("fields",)
+
+    fields: FrozenList["ObjectFieldNode"]
+
+
+class ObjectFieldNode(Node):
+    __slots__ = "name", "value"
+
+    name: NameNode
+    value: ValueNode
+
+
+# Directives
+
+
+class DirectiveNode(Node):
+    __slots__ = "name", "arguments"
+
+    name: NameNode
+    arguments: FrozenList[ArgumentNode]
+
+
+# Type Reference
+
+
+class TypeNode(Node):
+    __slots__ = ()
+
+
+class NamedTypeNode(TypeNode):
+    __slots__ = ("name",)
+
+    name: NameNode
+
+
+class ListTypeNode(TypeNode):
+    __slots__ = ("type",)
+
+    type: TypeNode
+
+
+class NonNullTypeNode(TypeNode):
+    __slots__ = ("type",)
+
+    type: Union[NamedTypeNode, ListTypeNode]
+
+
+# Type System Definition
+
+
+class TypeSystemDefinitionNode(DefinitionNode):
+    __slots__ = ()
+
+
+class SchemaDefinitionNode(TypeSystemDefinitionNode):
+    __slots__ = "description", "directives", "operation_types"
+
+    description: Optional[StringValueNode]
+    directives: FrozenList[DirectiveNode]
+    operation_types: FrozenList["OperationTypeDefinitionNode"]
+
+
+class OperationTypeDefinitionNode(Node):
+    __slots__ = "operation", "type"
+
+    operation: OperationType
+    type: NamedTypeNode
+
+
+# Type Definition
+
+
+class TypeDefinitionNode(TypeSystemDefinitionNode):
+    __slots__ = "description", "name", "directives"
+
+    description: Optional[StringValueNode]
+    name: NameNode
+    directives: FrozenList[DirectiveNode]
+
+
+class ScalarTypeDefinitionNode(TypeDefinitionNode):
+    __slots__ = ()
+
+
+class ObjectTypeDefinitionNode(TypeDefinitionNode):
+    __slots__ = "interfaces", "fields"
+
+    interfaces: FrozenList[NamedTypeNode]
+    fields: FrozenList["FieldDefinitionNode"]
+
+
+class FieldDefinitionNode(DefinitionNode):
+    __slots__ = "description", "name", "directives", "arguments", "type"
+
+    description: Optional[StringValueNode]
+    name: NameNode
+    directives: FrozenList[DirectiveNode]
+    arguments: FrozenList["InputValueDefinitionNode"]
+    type: TypeNode
+
+
+class InputValueDefinitionNode(DefinitionNode):
+    __slots__ = "description", "name", "directives", "type", "default_value"
+
+    description: Optional[StringValueNode]
+    name: NameNode
+    directives: FrozenList[DirectiveNode]
+    type: TypeNode
+    default_value: Optional[ValueNode]
+
+
+class InterfaceTypeDefinitionNode(TypeDefinitionNode):
+    __slots__ = "fields", "interfaces"
+
+    fields: FrozenList["FieldDefinitionNode"]
+    interfaces: FrozenList[NamedTypeNode]
+
+
+class UnionTypeDefinitionNode(TypeDefinitionNode):
+    __slots__ = ("types",)
+
+    types: FrozenList[NamedTypeNode]
+
+
+class EnumTypeDefinitionNode(TypeDefinitionNode):
+    __slots__ = ("values",)
+
+    values: FrozenList["EnumValueDefinitionNode"]
+
+
+class EnumValueDefinitionNode(TypeDefinitionNode):
+    __slots__ = ()
+
+
+class InputObjectTypeDefinitionNode(TypeDefinitionNode):
+    __slots__ = ("fields",)
+
+    fields: FrozenList[InputValueDefinitionNode]
+
+
+# Directive Definitions
+
+
+class DirectiveDefinitionNode(TypeSystemDefinitionNode):
+    __slots__ = "description", "name", "arguments", "repeatable", "locations"
+
+    description: Optional[StringValueNode]
+    name: NameNode
+    arguments: FrozenList[InputValueDefinitionNode]
+    repeatable: bool
+    locations: FrozenList[NameNode]
+
+
+# Type System Extensions
+
+
+class SchemaExtensionNode(Node):
+    __slots__ = "directives", "operation_types"
+
+    directives: FrozenList[DirectiveNode]
+    operation_types: FrozenList[OperationTypeDefinitionNode]
+
+
+# Type Extensions
+
+
+class TypeExtensionNode(TypeSystemDefinitionNode):
+    __slots__ = "name", "directives"
+
+    name: NameNode
+    directives: FrozenList[DirectiveNode]
+
+
+TypeSystemExtensionNode = Union[SchemaExtensionNode, TypeExtensionNode]
+
+
+class ScalarTypeExtensionNode(TypeExtensionNode):
+    __slots__ = ()
+
+
+class ObjectTypeExtensionNode(TypeExtensionNode):
+    __slots__ = "interfaces", "fields"
+
+    interfaces: FrozenList[NamedTypeNode]
+    fields: FrozenList[FieldDefinitionNode]
+
+
+class InterfaceTypeExtensionNode(TypeExtensionNode):
+    __slots__ = "interfaces", "fields"
+
+    interfaces: FrozenList[NamedTypeNode]
+    fields: FrozenList[FieldDefinitionNode]
+
+
+class UnionTypeExtensionNode(TypeExtensionNode):
+    __slots__ = ("types",)
+
+    types: FrozenList[NamedTypeNode]
+
+
+class EnumTypeExtensionNode(TypeExtensionNode):
+    __slots__ = ("values",)
+
+    values: FrozenList[EnumValueDefinitionNode]
+
+
+class InputObjectTypeExtensionNode(TypeExtensionNode):
+    __slots__ = ("fields",)
+
+    fields: FrozenList[InputValueDefinitionNode]
diff --git a/src/graphql/language/block_string.py b/src/graphql/language/block_string.py
new file mode 100644
index 0000000..3634f32
--- /dev/null
+++ b/src/graphql/language/block_string.py
@@ -0,0 +1,99 @@
+from typing import List
+
+__all__ = [
+    "dedent_block_string_value",
+    "print_block_string",
+    "get_block_string_indentation",
+]
+
+
+def dedent_block_string_value(raw_string: str) -> str:
+    """Produce the value of a block string from its parsed raw value.
+
+    Similar to CoffeeScript's block string, Python's docstring trim or Ruby's
+    strip_heredoc.
+
+    This implements the GraphQL spec's BlockStringValue() static algorithm.
+
+    For internal use only.
+    """
+    # Expand a block string's raw value into independent lines.
+    lines = raw_string.splitlines()
+
+    # Remove common indentation from all lines but first.
+    common_indent = get_block_string_indentation(lines)
+
+    if common_indent:
+        lines[1:] = [line[common_indent:] for line in lines[1:]]
+
+    # Remove leading and trailing blank lines.
+    while lines and not lines[0].strip():
+        lines = lines[1:]
+
+    while lines and not lines[-1].strip():
+        lines = lines[:-1]
+
+    # Return a string of the lines joined with U+000A.
+    return "\n".join(lines)
+
+
+def get_block_string_indentation(lines: List[str]) -> int:
+    """Get the amount of indentation for the given block string.
+
+    For internal use only.
+    """
+    common_indent = None
+
+    for line in lines[1:]:
+        indent = leading_whitespace(line)
+        if indent == len(line):
+            continue  # skip empty lines
+
+        if common_indent is None or indent < common_indent:
+            common_indent = indent
+            if common_indent == 0:
+                break
+
+    return 0 if common_indent is None else common_indent
+
+
+def leading_whitespace(s: str) -> int:
+    i = 0
+    n = len(s)
+    while i < n and s[i] in " \t":
+        i += 1
+    return i
+
+
+def print_block_string(
+    value: str, indentation: str = "", prefer_multiple_lines: bool = False
+) -> str:
+    """Print a block string in the indented block form.
+
+    Prints a block string in the indented block form by adding a leading and
+    trailing blank line. However, if a block string starts with whitespace and
+    is a single-line, adding a leading blank line would strip that whitespace.
+
+    For internal use only.
+    """
+    is_single_line = "\n" not in value
+    has_leading_space = value.startswith(" ") or value.startswith("\t")
+    has_trailing_quote = value.endswith('"')
+    has_trailing_slash = value.endswith("\\")
+    print_as_multiple_lines = (
+        not is_single_line
+        or has_trailing_quote
+        or has_trailing_slash
+        or prefer_multiple_lines
+    )
+
+    # Format a multi-line block quote to account for leading space.
+    if print_as_multiple_lines and not (is_single_line and has_leading_space):
+        result = "\n" + indentation
+    else:
+        result = ""
+    result += value.replace("\n", "\n" + indentation) if indentation else value
+    if print_as_multiple_lines:
+        result += "\n"
+
+    return '"""' + result.replace('"""', '\\"""') + '"""'
diff --git a/src/graphql/language/directive_locations.py b/src/graphql/language/directive_locations.py
new file mode 100644
index 0000000..dfce34d
--- /dev/null
+++ b/src/graphql/language/directive_locations.py
@@ -0,0 +1,30 @@
+from enum import Enum
+
+__all__ = ["DirectiveLocation"]
+
+
+class DirectiveLocation(Enum):
+    """The enum type representing the directive location values."""
+
+    # Request Definitions
+    QUERY = "query"
+    MUTATION = "mutation"
+    SUBSCRIPTION = "subscription"
+    FIELD = "field"
+    FRAGMENT_DEFINITION = "fragment definition"
+    FRAGMENT_SPREAD = "fragment spread"
+    VARIABLE_DEFINITION = "variable definition"
+    INLINE_FRAGMENT = "inline fragment"
+
+    # Type System Definitions
+    SCHEMA = "schema"
+    SCALAR = "scalar"
+    OBJECT = "object"
+    FIELD_DEFINITION = "field definition"
+    ARGUMENT_DEFINITION = "argument definition"
+    INTERFACE = "interface"
+    UNION = "union"
+    ENUM = "enum"
+    ENUM_VALUE = "enum value"
+    INPUT_OBJECT = "input object"
+    INPUT_FIELD_DEFINITION = "input field definition"
diff --git a/src/graphql/language/lexer.py b/src/graphql/language/lexer.py
new file mode 100644
index 0000000..6459e0a
--- /dev/null
+++ b/src/graphql/language/lexer.py
@@ -0,0 +1,449 @@
+from typing import List
+
+from ..error import GraphQLSyntaxError
+from .ast import Token
+from .block_string import dedent_block_string_value
+from .source import Source
+from .token_kind import TokenKind
+
+__all__ = ["Lexer", "is_punctuator_token_kind"]
+
+
+_punctuator_token_kinds = frozenset(
+    [
+        TokenKind.BANG,
+        TokenKind.DOLLAR,
+        TokenKind.AMP,
+        TokenKind.PAREN_L,
+        TokenKind.PAREN_R,
+        TokenKind.SPREAD,
+        TokenKind.COLON,
+        TokenKind.EQUALS,
+        TokenKind.AT,
+        TokenKind.BRACKET_L,
+        TokenKind.BRACKET_R,
+        TokenKind.BRACE_L,
+        TokenKind.PIPE,
+        TokenKind.BRACE_R,
+    ]
+)
+
+
+def is_punctuator_token_kind(kind: TokenKind) -> bool:
+    """Check whether the given token kind corresponds to a punctuator.
+
+    For internal use only.
+    """
+    return kind in _punctuator_token_kinds
+
+
+def print_char(char: str) -> str:
+    return repr(char) if char else TokenKind.EOF.value
+
+
+_KIND_FOR_PUNCT = {
+    "!": TokenKind.BANG,
+    "$": TokenKind.DOLLAR,
+    "&": TokenKind.AMP,
+    "(": TokenKind.PAREN_L,
+    ")": TokenKind.PAREN_R,
+    ":": TokenKind.COLON,
+    "=": TokenKind.EQUALS,
+    "@": TokenKind.AT,
+    "[": TokenKind.BRACKET_L,
+    "]": TokenKind.BRACKET_R,
+    "{": TokenKind.BRACE_L,
+    "}": TokenKind.BRACE_R,
+    "|": TokenKind.PIPE,
+}
+
+
+class Lexer:
+    """GraphQL Lexer
+
+    A Lexer is a stateful stream generator in that every time it is advanced, it returns
+    the next token in the Source. Assuming the source lexes, the final Token emitted by
+    the lexer will be of kind EOF, after which the lexer will repeatedly return the same
+    EOF token whenever called.
+    """
+
+    def __init__(self, source: Source):
+        """Given a Source object, initialize a Lexer for that source."""
+        self.source = source
+        self.token = self.last_token = Token(TokenKind.SOF, 0, 0, 0, 0)
+        self.line, self.line_start = 1, 0
+
+    def advance(self) -> Token:
+        """Advance the token stream to the next non-ignored token."""
+        self.last_token = self.token
+        token = self.token = self.lookahead()
+        return token
+
+    def lookahead(self) -> Token:
+        """Look ahead and return the next non-ignored token, but do not change state."""
+        token = self.token
+        if token.kind != TokenKind.EOF:
+            while True:
+                if not token.next:
+                    token.next = self.read_token(token)
+                token = token.next
+                if token.kind != TokenKind.COMMENT:
+                    break
+        return token
+
+    def read_token(self, prev: Token) -> Token:
+        """Get the next token from the source starting at the given position.
+
+        This skips over whitespace until it finds the next lexable token, then lexes
+        punctuators immediately or calls the appropriate helper function for more
+        complicated tokens.
+        """
+        source = self.source
+        body = source.body
+        body_length = len(body)
+
+        pos = self.position_after_whitespace(body, prev.end)
+        line = self.line
+        col = 1 + pos - self.line_start
+
+        if pos >= body_length:
+            return Token(TokenKind.EOF, body_length, body_length, line, col, prev)
+
+        char = body[pos]
+        kind = _KIND_FOR_PUNCT.get(char)
+        if kind:
+            return Token(kind, pos, pos + 1, line, col, prev)
+        if char == "#":
+            return self.read_comment(pos, line, col, prev)
+        elif char == ".":
+            if body[pos + 1 : pos + 3] == "..":
+                return Token(TokenKind.SPREAD, pos, pos + 3, line, col, prev)
+        elif "A" <= char <= "Z" or "a" <= char <= "z" or char == "_":
+            return self.read_name(pos, line, col, prev)
+        elif "0" <= char <= "9" or char == "-":
+            return self.read_number(pos, char, line, col, prev)
+        elif char == '"':
+            if body[pos + 1 : pos + 3] == '""':
+                return self.read_block_string(pos, line, col, prev)
+            return self.read_string(pos, line, col, prev)
+
+        raise GraphQLSyntaxError(source, pos, unexpected_character_message(char))
+
+    def position_after_whitespace(self, body: str, start_position: int) -> int:
+        """Go to next position after a whitespace.
+
+        Reads from body starting at start_position until it finds a non-whitespace
+        character, then returns the position of that character for lexing.
+        """
+        body_length = len(body)
+        position = start_position
+        while position < body_length:
+            char = body[position]
+            if char in " \t,\ufeff":
+                position += 1
+            elif char == "\n":
+                position += 1
+                self.line += 1
+                self.line_start = position
+            elif char == "\r":  # pragma: no cover
+                if body[position + 1 : position + 2] == "\n":
+                    position += 2
+                else:
+                    position += 1
+                self.line += 1
+                self.line_start = position
+            else:  # pragma: no cover
+                break
+        return position
+
+    def read_comment(self, start: int, line: int, col: int, prev: Token) -> Token:
+        """Read a comment token from the source file."""
+        body = self.source.body
+        body_length = len(body)
+
+        position = start
+        while True:
+            position += 1
+            if position >= body_length:
+                break
+            char = body[position]
+            if char < " " and char != "\t":
+                break
+        return Token(
+            TokenKind.COMMENT,
+            start,
+            position,
+            line,
+            col,
+            prev,
+            body[start + 1 : position],
+        )
+
+    def read_number(
+        self, start: int, char: str, line: int, col: int, prev: Token
+    ) -> Token:
+        """Reads a number token from the source file.
+
+        Either a float or an int depending on whether a decimal point appears.
+        """
+        source = self.source
+        body = source.body
+        position = start
+        is_float = False
+        if char == "-":
+            position += 1
+            char = body[position : position + 1]
+        if char == "0":
+            position += 1
+            char = body[position : position + 1]
+            if "0" <= char <= "9":
+                raise GraphQLSyntaxError(
+                    source,
+                    position,
+                    f"Invalid number, unexpected digit after 0: {print_char(char)}.",
+                )
+        else:
+            position = self.read_digits(position, char)
+            char = body[position : position + 1]
+        if char == ".":
+            is_float = True
+            position += 1
+            char = body[position : position + 1]
+            position = self.read_digits(position, char)
+            char = body[position : position + 1]
+        if char and char in "Ee":
+            is_float = True
+            position += 1
+            char = body[position : position + 1]
+            if char and char in "+-":
+                position += 1
+                char = body[position : position + 1]
+            position = self.read_digits(position, char)
+            char = body[position : position + 1]
+
+        # Numbers cannot be followed by . or NameStart
+        if char and (char == "." or is_name_start(char)):
+            raise GraphQLSyntaxError(
+                source,
+                position,
+                f"Invalid number, expected digit but got: {print_char(char)}.",
+            )
+
+        return Token(
+            TokenKind.FLOAT if is_float else TokenKind.INT,
+            start,
+            position,
+            line,
+            col,
+            prev,
+            body[start:position],
+        )
+
+    def read_digits(self, start: int, char: str) -> int:
+        """Return the new position in the source after reading digits."""
+        source = self.source
+        body = source.body
+        position = start
+        while "0" <= char <= "9":
+            position += 1
+            char = body[position : position + 1]
+        if position == start:
+            raise GraphQLSyntaxError(
+                source,
+                position,
+                f"Invalid number, expected digit but got: {print_char(char)}.",
+            )
+        return position
+
+    def read_string(self, start: int, line: int, col: int, prev: Token) -> Token:
+        """Read a string token from the source file."""
+        source = self.source
+        body = source.body
+        body_length = len(body)
+        position = start + 1
+        chunk_start = position
+        value: List[str] = []
+        append = value.append
+
+        while position < body_length:
+            char = body[position]
+            if char in "\n\r":
+                break
+            if char == '"':
+                append(body[chunk_start:position])
+                return Token(
+                    TokenKind.STRING,
+                    start,
+                    position + 1,
+                    line,
+                    col,
+                    prev,
+                    "".join(value),
+                )
+            if char < " " and char != "\t":
+                raise GraphQLSyntaxError(
+                    source,
+                    position,
+                    f"Invalid character within String: {print_char(char)}.",
+                )
+            position += 1
+            if char == "\\":
+                append(body[chunk_start : position - 1])
+                char = body[position : position + 1]
+                escaped = _ESCAPED_CHARS.get(char)
+                if escaped:
+                    value.append(escaped)
+                elif char == "u" and position + 4 < body_length:
+                    code = uni_char_code(*body[position + 1 : position + 5])
+                    if code < 0:
+                        escape = repr(body[position : position + 5])
+                        escape = escape[:1] + "\\" + escape[1:]
+                        raise GraphQLSyntaxError(
+                            source,
+                            position,
+                            f"Invalid character escape sequence: {escape}.",
+                        )
+                    append(chr(code))
+                    position += 4
+                else:
+                    escape = repr(char)
+                    escape = escape[:1] + "\\" + escape[1:]
+                    raise GraphQLSyntaxError(
+                        source,
+                        position,
+                        f"Invalid character escape sequence: {escape}.",
+                    )
+                position += 1
+                chunk_start = position
+
+        raise GraphQLSyntaxError(source, position, "Unterminated string.")
+
+    def read_block_string(self, start: int, line: int, col: int, prev: Token) -> Token:
+        source = self.source
+        body = source.body
+        body_length = len(body)
+        position = start + 3
+        chunk_start = position
+        raw_value = ""
+
+        while position < body_length:
+            char = body[position]
+            if char == '"' and body[position + 1 : position + 3] == '""':
+                raw_value += body[chunk_start:position]
+                return Token(
+                    TokenKind.BLOCK_STRING,
+                    start,
+                    position + 3,
+                    line,
+                    col,
+                    prev,
+                    dedent_block_string_value(raw_value),
+                )
+            if char < " " and char not in "\t\n\r":
+                raise GraphQLSyntaxError(
+                    source,
+                    position,
+                    f"Invalid character within String: {print_char(char)}.",
+                )
+
+            if char == "\n":
+                position += 1
+                self.line += 1
+                self.line_start = position
+            elif char == "\r":
+                if body[position + 1 : position + 2] == "\n":
+                    position += 2
+                else:
+                    position += 1
+                self.line += 1
+                self.line_start = position
+            elif char == "\\" and body[position + 1 : position + 4] == '"""':
+                raw_value += body[chunk_start:position] + '"""'
+                position += 4
+                chunk_start = position
+            else:
+                position += 1
+
+        raise GraphQLSyntaxError(source, position, "Unterminated string.")
+
+    def read_name(self, start: int, line: int, col: int, prev: Token) -> Token:
+        """Read an alphanumeric + underscore name from the source."""
+        body = self.source.body
+        body_length = len(body)
+        position = start + 1
+        while position < body_length:
+            char = body[position]
+            if not (
+                char == "_"
+                or "0" <= char <= "9"
+                or "A" <= char <= "Z"
+                or "a" <= char <= "z"
+            ):
+                break
+            position += 1
+        return Token(
+            TokenKind.NAME, start, position, line, col, prev, body[start:position]
+        )
+
+
+_ESCAPED_CHARS = {
+    '"': '"',
+    "/": "/",
+    "\\": "\\",
+    "b": "\b",
+    "f": "\f",
+    "n": "\n",
+    "r": "\r",
+    "t": "\t",
+}
+
+
+def unexpected_character_message(char: str) -> str:
+    """Report a message that an unexpected character was encountered."""
+    if char < " " and char not in "\t\n\r":
+        return f"Cannot contain the invalid character {print_char(char)}."
+    if char == "'":
+        return (
+            "Unexpected single quote character ('),"
+            ' did you mean to use a double quote (")?'
+        )
+    return f"Cannot parse the unexpected character {print_char(char)}."
+
+
+def uni_char_code(a: str, b: str, c: str, d: str) -> int:
+    """Convert unicode characters to integers.
+
+    Converts four hexadecimal chars to the integer that the string represents.
+    For example, uni_char_code('0','0','0','f') will return 15,
+    and uni_char_code('0','0','f','f') returns 255.
+
+    Returns a negative number on error, if a char was invalid.
+
+    This is implemented by noting that char2hex() returns -1 on error,
+    which means the result of ORing the char2hex() will also be negative.
+    """
+    return char2hex(a) << 12 | char2hex(b) << 8 | char2hex(c) << 4 | char2hex(d)
+
+
+def char2hex(a: str) -> int:
+    """Convert a hex character to its integer value.
+
+    '0' becomes 0, '9' becomes 9
+    'A' becomes 10, 'F' becomes 15
+    'a' becomes 10, 'f' becomes 15
+
+    Returns -1 on error.
+
+    """
+    if "0" <= a <= "9":
+        return ord(a) - 48
+    elif "A" <= a <= "F":
+        return ord(a) - 55
+    elif "a" <= a <= "f":  # a-f
+        return ord(a) - 87
+    return -1
+
+
+def is_name_start(char: str) -> bool:
+    """Check whether char is an underscore or a plain ASCII letter"""
+    return char == "_" or "A" <= char <= "Z" or "a" <= char <= "z"
diff --git a/src/graphql/language/location.py b/src/graphql/language/location.py
new file mode 100644
index 0000000..1aaee98
--- /dev/null
+++ b/src/graphql/language/location.py
@@ -0,0 +1,34 @@
+from typing import Any, Dict, NamedTuple, TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from .source import Source  # noqa: F401
+
+__all__ = ["get_location", "SourceLocation"]
+
+
+class SourceLocation(NamedTuple):
+    """Represents a location in a Source."""
+
+    line: int
+    column: int
+
+    @property
+    def formatted(self) -> Dict[str, int]:
+        return dict(line=self.line, column=self.column)
+
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, dict):
+            return self.formatted == other
+        return tuple(self) == other
+
+    def __ne__(self, other: Any) -> bool:
+        return not self == other
+
+
+def get_location(source: "Source", position: int) -> SourceLocation:
+    """Get the line and column for a character position in the source.
+
+    Takes a Source and a UTF-8 character offset, and returns the corresponding line and
+    column as a SourceLocation.
+    """
+    return source.get_location(position)
diff --git a/src/graphql/language/parser.py b/src/graphql/language/parser.py
new file mode 100644
index 0000000..10509f0
--- /dev/null
+++ b/src/graphql/language/parser.py
@@ -0,0 +1,1114 @@
+from typing import Callable, Dict, List, Optional, Union, TypeVar, cast
+from functools import partial
+
+from .ast import (
+    ArgumentNode,
+    BooleanValueNode,
+    DefinitionNode,
+    DirectiveDefinitionNode,
+    DirectiveNode,
+    DocumentNode,
+    EnumTypeDefinitionNode,
+    EnumTypeExtensionNode,
+    EnumValueDefinitionNode,
+    EnumValueNode,
+    FieldDefinitionNode,
+    FieldNode,
+    FloatValueNode,
+    FragmentDefinitionNode,
+    FragmentSpreadNode,
+    InlineFragmentNode,
+    InputObjectTypeDefinitionNode,
+    InputObjectTypeExtensionNode,
+    InputValueDefinitionNode,
+    IntValueNode,
+    InterfaceTypeDefinitionNode,
+    InterfaceTypeExtensionNode,
+    ListTypeNode,
+    ListValueNode,
+    Location,
+    NameNode,
+    NamedTypeNode,
+    NonNullTypeNode,
+    NullValueNode,
+    ObjectFieldNode,
+    ObjectTypeDefinitionNode,
+    ObjectTypeExtensionNode,
+    ObjectValueNode,
+    OperationDefinitionNode,
+    OperationType,
+    OperationTypeDefinitionNode,
+    ScalarTypeDefinitionNode,
+    ScalarTypeExtensionNode,
+    SchemaDefinitionNode,
+    SchemaExtensionNode,
+    SelectionNode,
+    SelectionSetNode,
+    StringValueNode,
+    TypeNode,
+    TypeSystemDefinitionNode,
+    TypeSystemExtensionNode,
+    UnionTypeDefinitionNode,
+    UnionTypeExtensionNode,
+    ValueNode,
+    VariableDefinitionNode,
+    VariableNode,
+)
+from .directive_locations import DirectiveLocation
+from .ast import Token
+from .lexer import Lexer, is_punctuator_token_kind
+from .source import Source
+from .token_kind import TokenKind
+from ..error import GraphQLError, GraphQLSyntaxError
+from ..pyutils import inspect
+
+__all__ = ["parse", "parse_type", "parse_value"]
+
+T = TypeVar("T")
+
+SourceType = Union[Source, str]
+
+
+def parse(
+    source: SourceType,
+    no_location: bool = False,
+    experimental_fragment_variables: bool = False,
+) -> DocumentNode:
+    """Given a GraphQL source, parse it into a Document.
+
+    Throws GraphQLError if a syntax error is encountered.
+
+    By default, the parser creates AST nodes that know the location in the source that
+    they correspond to. The ``no_location`` option disables that behavior for
+    performance or testing.
+
+    Experimental features:
+
+    If ``experimental_fragment_variables`` is set to ``True``, the parser will
+    understand and parse variable definitions contained in a fragment definition.
+    They'll be represented in the
+    :attr:`~graphql.language.FragmentDefinitionNode.variable_definitions` field
+    of the :class:`~graphql.language.FragmentDefinitionNode`.
+
+    The syntax is identical to normal, query-defined variables. For example::
+
+        fragment A($var: Boolean = false) on T  {
+          ...
+        }
+    """
+    parser = Parser(
+        source,
+        no_location=no_location,
+        experimental_fragment_variables=experimental_fragment_variables,
+    )
+    return parser.parse_document()
+
+
+def parse_value(
+    source: SourceType,
+    no_location: bool = False,
+    experimental_fragment_variables: bool = False,
+) -> ValueNode:
+    """Parse the AST for a given string containing a GraphQL value.
+
+    Throws GraphQLError if a syntax error is encountered.
+
+    This is useful within tools that operate upon GraphQL Values directly and in
+    isolation of complete GraphQL documents.
+
+    Consider providing the results to the utility function:
+    :func:`~graphql.value_from_ast`.
+    """
+    parser = Parser(
+        source,
+        no_location=no_location,
+        experimental_fragment_variables=experimental_fragment_variables,
+    )
+    parser.expect_token(TokenKind.SOF)
+    value = parser.parse_value_literal(False)
+    parser.expect_token(TokenKind.EOF)
+    return value
+
+
+def parse_type(
+    source: SourceType,
+    no_location: bool = False,
+    experimental_fragment_variables: bool = False,
+) -> TypeNode:
+    """Parse the AST for a given string containing a GraphQL Type.
+
+    Throws GraphQLError if a syntax error is encountered.
+
+    This is useful within tools that operate upon GraphQL Types directly and
+    in isolation of complete GraphQL documents.
+
+    Consider providing the results to the utility function:
+    :func:`~graphql.value_from_ast`.
+    """
+    parser = Parser(
+        source,
+        no_location=no_location,
+        experimental_fragment_variables=experimental_fragment_variables,
+    )
+    parser.expect_token(TokenKind.SOF)
+    type_ = parser.parse_type_reference()
+    parser.expect_token(TokenKind.EOF)
+    return type_
+
+
+class Parser:
+
+    _lexer: Lexer
+    _no_Location: bool
+    _experimental_fragment_variables: bool
+
+    def __init__(
+        self,
+        source: SourceType,
+        no_location: bool = False,
+        experimental_fragment_variables: bool = False,
+    ):
+        if isinstance(source, str):
+            source = Source(source)
+        elif not isinstance(source, Source):
+            raise TypeError(f"Must provide Source. Received: {inspect(source)}.")
+        self._lexer = Lexer(source)
+        self._no_location = no_location
+        self._experimental_fragment_variables = experimental_fragment_variables
+
+    def parse_name(self) -> NameNode:
+        """Convert a name lex token into a name parse node."""
+        token = self.expect_token(TokenKind.NAME)
+        return NameNode(value=token.value, loc=self.loc(token))
+
+    # Implement the parsing rules in the Document section.
+
+    def parse_document(self) -> DocumentNode:
+        """Document: Definition+"""
+        start = self._lexer.token
+        return DocumentNode(
+            definitions=self.many(TokenKind.SOF, self.parse_definition, TokenKind.EOF),
+            loc=self.loc(start),
+        )
+
+    _parse_definition_method_names: Dict[str, str] = {
+        **dict.fromkeys(("query", "mutation", "subscription"), "operation_definition"),
+        "fragment": "fragment_definition",
+        **dict.fromkeys(
+            (
+                "schema",
+                "scalar",
+                "type",
+                "interface",
+                "union",
+                "enum",
+                "input",
+                "directive",
+            ),
+            "type_system_definition",
+        ),
+        "extend": "type_system_extension",
+    }
+
+    def parse_definition(self) -> DefinitionNode:
+        """Definition: ExecutableDefinition or TypeSystemDefinition/Extension
+
+        ExecutableDefinition: OperationDefinition or FragmentDefinition
+        """
+        if self.peek(TokenKind.NAME):
+            method_name = self._parse_definition_method_names.get(
+                cast(str, self._lexer.token.value)
+            )
+            if method_name:
+                return getattr(self, f"parse_{method_name}")()
+        elif self.peek(TokenKind.BRACE_L):
+            return self.parse_operation_definition()
+        elif self.peek_description():
+            return self.parse_type_system_definition()
+        raise self.unexpected()
+
+    _parse_executable_definition_method_names: Dict[str, str] = {
+        **dict.fromkeys(("query", "mutation", "subscription"), "operation_definition"),
+        **dict.fromkeys(("fragment",), "fragment_definition"),
+    }
+
+    # Implement the parsing rules in the Operations section.
+
+    def parse_operation_definition(self) -> OperationDefinitionNode:
+        """OperationDefinition"""
+        start = self._lexer.token
+        if self.peek(TokenKind.BRACE_L):
+            return OperationDefinitionNode(
+                operation=OperationType.QUERY,
+                name=None,
+                variable_definitions=[],
+                directives=[],
+                selection_set=self.parse_selection_set(),
+                loc=self.loc(start),
+            )
+        operation = self.parse_operation_type()
+        name = self.parse_name() if self.peek(TokenKind.NAME) else None
+        return OperationDefinitionNode(
+            operation=operation,
+            name=name,
+            variable_definitions=self.parse_variable_definitions(),
+            directives=self.parse_directives(False),
+            selection_set=self.parse_selection_set(),
+            loc=self.loc(start),
+        )
+
+    def parse_operation_type(self) -> OperationType:
+        """OperationType: one of query mutation subscription"""
+        operation_token = self.expect_token(TokenKind.NAME)
+        try:
+            return OperationType(operation_token.value)
+        except ValueError:
+            raise self.unexpected(operation_token)
+
+    def parse_variable_definitions(self) -> List[VariableDefinitionNode]:
+        """VariableDefinitions: (VariableDefinition+)"""
+        return self.optional_many(
+            TokenKind.PAREN_L, self.parse_variable_definition, TokenKind.PAREN_R
+        )
+
+    def parse_variable_definition(self) -> VariableDefinitionNode:
+        """VariableDefinition: Variable: Type DefaultValue? Directives[Const]?"""
+        start = self._lexer.token
+        return VariableDefinitionNode(
+            variable=self.parse_variable(),
+            type=self.expect_token(TokenKind.COLON) and self.parse_type_reference(),
+            default_value=self.parse_value_literal(True)
+            if self.expect_optional_token(TokenKind.EQUALS)
+            else None,
+            directives=self.parse_directives(True),
+            loc=self.loc(start),
+        )
+
+    def parse_variable(self) -> VariableNode:
+        """Variable: $Name"""
+        start = self._lexer.token
+        self.expect_token(TokenKind.DOLLAR)
+        return VariableNode(name=self.parse_name(), loc=self.loc(start))
+
+    def parse_selection_set(self) -> SelectionSetNode:
+        """SelectionSet: {Selection+}"""
+        start = self._lexer.token
+        return SelectionSetNode(
+            selections=self.many(
+                TokenKind.BRACE_L, self.parse_selection, TokenKind.BRACE_R
+            ),
+            loc=self.loc(start),
+        )
+
+    def parse_selection(self) -> SelectionNode:
+        """Selection: Field or FragmentSpread or InlineFragment"""
+        return (
+            self.parse_fragment if self.peek(TokenKind.SPREAD) else self.parse_field
+        )()
+
+    def parse_field(self) -> FieldNode:
+        """Field: Alias? Name Arguments? Directives? SelectionSet?"""
+        start = self._lexer.token
+        name_or_alias = self.parse_name()
+        if self.expect_optional_token(TokenKind.COLON):
+            alias: Optional[NameNode] = name_or_alias
+            name = self.parse_name()
+        else:
+            alias = None
+            name = name_or_alias
+        return FieldNode(
+            alias=alias,
+            name=name,
+            arguments=self.parse_arguments(False),
+            directives=self.parse_directives(False),
+            selection_set=self.parse_selection_set()
+            if self.peek(TokenKind.BRACE_L)
+            else None,
+            loc=self.loc(start),
+        )
+
+    def parse_arguments(self, is_const: bool) -> List[ArgumentNode]:
+        """Arguments[Const]: (Argument[?Const]+)"""
+        item = self.parse_const_argument if is_const else self.parse_argument
+        return self.optional_many(TokenKind.PAREN_L, item, TokenKind.PAREN_R)
+
+    def parse_argument(self) -> ArgumentNode:
+        """Argument: Name : Value"""
+        start = self._lexer.token
+        name = self.parse_name()
+
+        self.expect_token(TokenKind.COLON)
+        return ArgumentNode(
+            name=name, value=self.parse_value_literal(False), loc=self.loc(start)
+        )
+
+    def parse_const_argument(self) -> ArgumentNode:
+        """Argument[Const]: Name : Value[?Const]"""
+        start = self._lexer.token
+        return ArgumentNode(
+            name=self.parse_name(),
+            value=self.expect_token(TokenKind.COLON) and self.parse_value_literal(True),
+            loc=self.loc(start),
+        )
+
+    # Implement the parsing rules in the Fragments section.
+
+    def parse_fragment(self) -> Union[FragmentSpreadNode, InlineFragmentNode]:
+        """Corresponds to both FragmentSpread and InlineFragment in the spec.
+
+        FragmentSpread: ... FragmentName Directives?
+        InlineFragment: ... TypeCondition? Directives? SelectionSet
+        """
+        start = self._lexer.token
+        self.expect_token(TokenKind.SPREAD)
+
+        has_type_condition = self.expect_optional_keyword("on")
+        if not has_type_condition and self.peek(TokenKind.NAME):
+            return FragmentSpreadNode(
+                name=self.parse_fragment_name(),
+                directives=self.parse_directives(False),
+                loc=self.loc(start),
+            )
+        return InlineFragmentNode(
+            type_condition=self.parse_named_type() if has_type_condition else None,
+            directives=self.parse_directives(False),
+            selection_set=self.parse_selection_set(),
+            loc=self.loc(start),
+        )
+
+    def parse_fragment_definition(self) -> FragmentDefinitionNode:
+        """FragmentDefinition"""
+        start = self._lexer.token
+        self.expect_keyword("fragment")
+        # Experimental support for defining variables within fragments changes
+        # the grammar of FragmentDefinition
+        if self._experimental_fragment_variables:
+            return FragmentDefinitionNode(
+                name=self.parse_fragment_name(),
+                variable_definitions=self.parse_variable_definitions(),
+                type_condition=self.parse_type_condition(),
+                directives=self.parse_directives(False),
+                selection_set=self.parse_selection_set(),
+                loc=self.loc(start),
+            )
+        return FragmentDefinitionNode(
+            name=self.parse_fragment_name(),
+            type_condition=self.parse_type_condition(),
+            directives=self.parse_directives(False),
+            selection_set=self.parse_selection_set(),
+            loc=self.loc(start),
+        )
+
+    def parse_fragment_name(self) -> NameNode:
+        """FragmentName: Name but not ``on``"""
+        if self._lexer.token.value == "on":
+            raise self.unexpected()
+        return self.parse_name()
+
+    def parse_type_condition(self) -> NamedTypeNode:
+        """TypeCondition: NamedType"""
+        self.expect_keyword("on")
+        return self.parse_named_type()
+
+    # Implement the parsing rules in the Values section.
+
+    _parse_value_literal_method_names: Dict[TokenKind, str] = {
+        TokenKind.BRACKET_L: "list",
+        TokenKind.BRACE_L: "object",
+        TokenKind.INT: "int",
+        TokenKind.FLOAT: "float",
+        TokenKind.STRING: "string_literal",
+        TokenKind.BLOCK_STRING: "string_literal",
+        TokenKind.NAME: "named_values",
+        TokenKind.DOLLAR: "variable_value",
+    }
+
+    def parse_value_literal(self, is_const: bool) -> ValueNode:
+        method_name = self._parse_value_literal_method_names.get(self._lexer.token.kind)
+        if method_name:  # pragma: no cover
+            return getattr(self, f"parse_{method_name}")(is_const)
+        raise self.unexpected()  # pragma: no cover
+
+    def parse_string_literal(self, _is_const: bool = False) -> StringValueNode:
+        token = self._lexer.token
+        self._lexer.advance()
+        return StringValueNode(
+            value=token.value,
+            block=token.kind == TokenKind.BLOCK_STRING,
+            loc=self.loc(token),
+        )
+
+    def parse_list(self, is_const: bool) -> ListValueNode:
+        """ListValue[Const]"""
+        start = self._lexer.token
+        item = partial(self.parse_value_literal, is_const)
+        # noinspection PyTypeChecker
+        return ListValueNode(
+            values=self.any(TokenKind.BRACKET_L, item, TokenKind.BRACKET_R),
+            loc=self.loc(start),
+        )
+
+    def parse_object_field(self, is_const: bool) -> ObjectFieldNode:
+        start = self._lexer.token
+        name = self.parse_name()
+        self.expect_token(TokenKind.COLON)
+
+        return ObjectFieldNode(
+            name=name, value=self.parse_value_literal(is_const), loc=self.loc(start)
+        )
+
+    def parse_object(self, is_const: bool) -> ObjectValueNode:
+        """ObjectValue[Const]"""
+        start = self._lexer.token
+        item = partial(self.parse_object_field, is_const)
+        return ObjectValueNode(
+            fields=self.any(TokenKind.BRACE_L, item, TokenKind.BRACE_R),
+            loc=self.loc(start),
+        )
+
+    def parse_int(self, _is_const: bool = False) -> IntValueNode:
+        token = self._lexer.token
+        self._lexer.advance()
+        return IntValueNode(value=token.value, loc=self.loc(token))
+
+    def parse_float(self, _is_const: bool = False) -> FloatValueNode:
+        token = self._lexer.token
+        self._lexer.advance()
+        return FloatValueNode(value=token.value, loc=self.loc(token))
+
+    def parse_named_values(self, _is_const: bool = False) -> ValueNode:
+        token = self._lexer.token
+        value = token.value
+        self._lexer.advance()
+        if value == "true":
+            return BooleanValueNode(value=True, loc=self.loc(token))
+        if value == "false":
+            return BooleanValueNode(value=False, loc=self.loc(token))
+        if value == "null":
+            return NullValueNode(loc=self.loc(token))
+        return EnumValueNode(value=value, loc=self.loc(token))
+
+    def parse_variable_value(self, is_const: bool) -> VariableNode:
+        if not is_const:
+            return self.parse_variable()
+        raise self.unexpected()
+
+    # Implement the parsing rules in the Directives section.
+
+    def parse_directives(self, is_const: bool) -> List[DirectiveNode]:
+        """Directives[Const]: Directive[?Const]+"""
+        directives: List[DirectiveNode] = []
+        append = directives.append
+        while self.peek(TokenKind.AT):
+            append(self.parse_directive(is_const))
+        return directives
+
+    def parse_directive(self, is_const: bool) -> DirectiveNode:
+        """Directive[Const]: @ Name Arguments[?Const]?"""
+        start = self._lexer.token
+        self.expect_token(TokenKind.AT)
+        return DirectiveNode(
+            name=self.parse_name(),
+            arguments=self.parse_arguments(is_const),
+            loc=self.loc(start),
+        )
+
+    # Implement the parsing rules in the Types section.
+
+    def parse_type_reference(self) -> TypeNode:
+        """Type: NamedType or ListType or NonNullType"""
+        start = self._lexer.token
+        if self.expect_optional_token(TokenKind.BRACKET_L):
+            type_ = self.parse_type_reference()
+            self.expect_token(TokenKind.BRACKET_R)
+            type_ = ListTypeNode(type=type_, loc=self.loc(start))
+        else:
+            type_ = self.parse_named_type()
+        if self.expect_optional_token(TokenKind.BANG):
+            return NonNullTypeNode(type=type_, loc=self.loc(start))
+        return type_
+
+    def parse_named_type(self) -> NamedTypeNode:
+        """NamedType: Name"""
+        start = self._lexer.token
+        return NamedTypeNode(name=self.parse_name(), loc=self.loc(start))
+
+    # Implement the parsing rules in the Type Definition section.
+
+    _parse_type_system_definition_method_names: Dict[str, str] = {
+        "schema": "schema_definition",
+        "scalar": "scalar_type_definition",
+        "type": "object_type_definition",
+        "interface": "interface_type_definition",
+        "union": "union_type_definition",
+        "enum": "enum_type_definition",
+        "input": "input_object_type_definition",
+        "directive": "directive_definition",
+    }
+
+    def parse_type_system_definition(self) -> TypeSystemDefinitionNode:
+        """TypeSystemDefinition"""
+        # Many definitions begin with a description and require a lookahead.
+        keyword_token = (
+            self._lexer.lookahead() if self.peek_description() else self._lexer.token
+        )
+        method_name = self._parse_type_system_definition_method_names.get(
+            cast(str, keyword_token.value)
+        )
+        if method_name:
+            return getattr(self, f"parse_{method_name}")()
+        raise self.unexpected(keyword_token)
+
+    _parse_type_extension_method_names: Dict[str, str] = {
+        "schema": "schema_extension",
+        "scalar": "scalar_type_extension",
+        "type": "object_type_extension",
+        "interface": "interface_type_extension",
+        "union": "union_type_extension",
+        "enum": "enum_type_extension",
+        "input": "input_object_type_extension",
+    }
+
+    def parse_type_system_extension(self) -> TypeSystemExtensionNode:
+        """TypeSystemExtension"""
+        keyword_token = self._lexer.lookahead()
+        if keyword_token.kind == TokenKind.NAME:
+            method_name = self._parse_type_extension_method_names.get(
+                cast(str, keyword_token.value)
+            )
+            if method_name:  # pragma: no cover
+                return getattr(self, f"parse_{method_name}")()
+        raise self.unexpected(keyword_token)
+
+    def peek_description(self) -> bool:
+        return self.peek(TokenKind.STRING) or self.peek(TokenKind.BLOCK_STRING)
+
+    def parse_description(self) -> Optional[StringValueNode]:
+        """Description: StringValue"""
+        if self.peek_description():
+            return self.parse_string_literal()
+        return None
+
+    def parse_schema_definition(self) -> SchemaDefinitionNode:
+        """SchemaDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        self.expect_keyword("schema")
+        directives = self.parse_directives(True)
+        operation_types = self.many(
+            TokenKind.BRACE_L, self.parse_operation_type_definition, TokenKind.BRACE_R
+        )
+        return SchemaDefinitionNode(
+            description=description,
+            directives=directives,
+            operation_types=operation_types,
+            loc=self.loc(start),
+        )
+
+    def parse_operation_type_definition(self) -> OperationTypeDefinitionNode:
+        """OperationTypeDefinition: OperationType : NamedType"""
+        start = self._lexer.token
+        operation = self.parse_operation_type()
+        self.expect_token(TokenKind.COLON)
+        type_ = self.parse_named_type()
+        return OperationTypeDefinitionNode(
+            operation=operation, type=type_, loc=self.loc(start)
+        )
+
+    def parse_scalar_type_definition(self) -> ScalarTypeDefinitionNode:
+        """ScalarTypeDefinition: Description? scalar Name Directives[Const]?"""
+        start = self._lexer.token
+        description = self.parse_description()
+        self.expect_keyword("scalar")
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        return ScalarTypeDefinitionNode(
+            description=description,
+            name=name,
+            directives=directives,
+            loc=self.loc(start),
+        )
+
+    def parse_object_type_definition(self) -> ObjectTypeDefinitionNode:
+        """ObjectTypeDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        self.expect_keyword("type")
+        name = self.parse_name()
+        interfaces = self.parse_implements_interfaces()
+        directives = self.parse_directives(True)
+        fields = self.parse_fields_definition()
+        return ObjectTypeDefinitionNode(
+            description=description,
+            name=name,
+            interfaces=interfaces,
+            directives=directives,
+            fields=fields,
+            loc=self.loc(start),
+        )
+
+    def parse_implements_interfaces(self) -> List[NamedTypeNode]:
+        """ImplementsInterfaces"""
+        types: List[NamedTypeNode] = []
+        if self.expect_optional_keyword("implements"):
+            # optional leading ampersand
+            self.expect_optional_token(TokenKind.AMP)
+            append = types.append
+            while True:
+                append(self.parse_named_type())
+                if not self.expect_optional_token(TokenKind.AMP):
+                    break
+        return types
+
+    def parse_fields_definition(self) -> List[FieldDefinitionNode]:
+        """FieldsDefinition: {FieldDefinition+}"""
+        return self.optional_many(
+            TokenKind.BRACE_L, self.parse_field_definition, TokenKind.BRACE_R
+        )
+
+    def parse_field_definition(self) -> FieldDefinitionNode:
+        """FieldDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        name = self.parse_name()
+        args = self.parse_argument_defs()
+        self.expect_token(TokenKind.COLON)
+        type_ = self.parse_type_reference()
+        directives = self.parse_directives(True)
+        return FieldDefinitionNode(
+            description=description,
+            name=name,
+            arguments=args,
+            type=type_,
+            directives=directives,
+            loc=self.loc(start),
+        )
+
+    def parse_argument_defs(self) -> List[InputValueDefinitionNode]:
+        """ArgumentsDefinition: (InputValueDefinition+)"""
+        return self.optional_many(
+            TokenKind.PAREN_L, self.parse_input_value_def, TokenKind.PAREN_R
+        )
+
+    def parse_input_value_def(self) -> InputValueDefinitionNode:
+        """InputValueDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        name = self.parse_name()
+        self.expect_token(TokenKind.COLON)
+        type_ = self.parse_type_reference()
+        default_value = (
+            self.parse_value_literal(True)
+            if self.expect_optional_token(TokenKind.EQUALS)
+            else None
+        )
+        directives = self.parse_directives(True)
+        return InputValueDefinitionNode(
+            description=description,
+            name=name,
+            type=type_,
+            default_value=default_value,
+            directives=directives,
+            loc=self.loc(start),
+        )
+
+    def parse_interface_type_definition(self) -> InterfaceTypeDefinitionNode:
+        """InterfaceTypeDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        self.expect_keyword("interface")
+        name = self.parse_name()
+        interfaces = self.parse_implements_interfaces()
+        directives = self.parse_directives(True)
+        fields = self.parse_fields_definition()
+        return InterfaceTypeDefinitionNode(
+            description=description,
+            name=name,
+            interfaces=interfaces,
+            directives=directives,
+            fields=fields,
+            loc=self.loc(start),
+        )
+
+    def parse_union_type_definition(self) -> UnionTypeDefinitionNode:
+        """UnionTypeDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        self.expect_keyword("union")
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        types = self.parse_union_member_types()
+        return UnionTypeDefinitionNode(
+            description=description,
+            name=name,
+            directives=directives,
+            types=types,
+            loc=self.loc(start),
+        )
+
+    def parse_union_member_types(self) -> List[NamedTypeNode]:
+        """UnionMemberTypes"""
+        types: List[NamedTypeNode] = []
+        if self.expect_optional_token(TokenKind.EQUALS):
+            # optional leading pipe
+            self.expect_optional_token(TokenKind.PIPE)
+            append = types.append
+            while True:
+                append(self.parse_named_type())
+                if not self.expect_optional_token(TokenKind.PIPE):
+                    break
+        return types
+
+    def parse_enum_type_definition(self) -> EnumTypeDefinitionNode:
+        """UnionTypeDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        self.expect_keyword("enum")
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        values = self.parse_enum_values_definition()
+        return EnumTypeDefinitionNode(
+            description=description,
+            name=name,
+            directives=directives,
+            values=values,
+            loc=self.loc(start),
+        )
+
+    def parse_enum_values_definition(self) -> List[EnumValueDefinitionNode]:
+        """EnumValuesDefinition: {EnumValueDefinition+}"""
+        return self.optional_many(
+            TokenKind.BRACE_L, self.parse_enum_value_definition, TokenKind.BRACE_R
+        )
+
+    def parse_enum_value_definition(self) -> EnumValueDefinitionNode:
+        """EnumValueDefinition: Description? EnumValue Directives[Const]?"""
+        start = self._lexer.token
+        description = self.parse_description()
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        return EnumValueDefinitionNode(
+            description=description,
+            name=name,
+            directives=directives,
+            loc=self.loc(start),
+        )
+
+    def parse_input_object_type_definition(self) -> InputObjectTypeDefinitionNode:
+        """InputObjectTypeDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        self.expect_keyword("input")
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        fields = self.parse_input_fields_definition()
+        return InputObjectTypeDefinitionNode(
+            description=description,
+            name=name,
+            directives=directives,
+            fields=fields,
+            loc=self.loc(start),
+        )
+
+    def parse_input_fields_definition(self) -> List[InputValueDefinitionNode]:
+        """InputFieldsDefinition: {InputValueDefinition+}"""
+        return self.optional_many(
+            TokenKind.BRACE_L, self.parse_input_value_def, TokenKind.BRACE_R
+        )
+
+    def parse_schema_extension(self) -> SchemaExtensionNode:
+        """SchemaExtension"""
+        start = self._lexer.token
+        self.expect_keyword("extend")
+        self.expect_keyword("schema")
+        directives = self.parse_directives(True)
+        operation_types = self.optional_many(
+            TokenKind.BRACE_L, self.parse_operation_type_definition, TokenKind.BRACE_R
+        )
+        if not directives and not operation_types:
+            raise self.unexpected()
+        return SchemaExtensionNode(
+            directives=directives, operation_types=operation_types, loc=self.loc(start)
+        )
+
+    def parse_scalar_type_extension(self) -> ScalarTypeExtensionNode:
+        """ScalarTypeExtension"""
+        start = self._lexer.token
+        self.expect_keyword("extend")
+        self.expect_keyword("scalar")
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        if not directives:
+            raise self.unexpected()
+        return ScalarTypeExtensionNode(
+            name=name, directives=directives, loc=self.loc(start)
+        )
+
+    def parse_object_type_extension(self) -> ObjectTypeExtensionNode:
+        """ObjectTypeExtension"""
+        start = self._lexer.token
+        self.expect_keyword("extend")
+        self.expect_keyword("type")
+        name = self.parse_name()
+        interfaces = self.parse_implements_interfaces()
+        directives = self.parse_directives(True)
+        fields = self.parse_fields_definition()
+        if not (interfaces or directives or fields):
+            raise self.unexpected()
+        return ObjectTypeExtensionNode(
+            name=name,
+            interfaces=interfaces,
+            directives=directives,
+            fields=fields,
+            loc=self.loc(start),
+        )
+
+    def parse_interface_type_extension(self) -> InterfaceTypeExtensionNode:
+        """InterfaceTypeExtension"""
+        start = self._lexer.token
+        self.expect_keyword("extend")
+        self.expect_keyword("interface")
+        name = self.parse_name()
+        interfaces = self.parse_implements_interfaces()
+        directives = self.parse_directives(True)
+        fields = self.parse_fields_definition()
+        if not (interfaces or directives or fields):
+            raise self.unexpected()
+        return InterfaceTypeExtensionNode(
+            name=name,
+            interfaces=interfaces,
+            directives=directives,
+            fields=fields,
+            loc=self.loc(start),
+        )
+
+    def parse_union_type_extension(self) -> UnionTypeExtensionNode:
+        """UnionTypeExtension"""
+        start = self._lexer.token
+        self.expect_keyword("extend")
+        self.expect_keyword("union")
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        types = self.parse_union_member_types()
+        if not (directives or types):
+            raise self.unexpected()
+        return UnionTypeExtensionNode(
+            name=name, directives=directives, types=types, loc=self.loc(start)
+        )
+
+    def parse_enum_type_extension(self) -> EnumTypeExtensionNode:
+        """EnumTypeExtension"""
+        start = self._lexer.token
+        self.expect_keyword("extend")
+        self.expect_keyword("enum")
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        values = self.parse_enum_values_definition()
+        if not (directives or values):
+            raise self.unexpected()
+        return EnumTypeExtensionNode(
+            name=name, directives=directives, values=values, loc=self.loc(start)
+        )
+
+    def parse_input_object_type_extension(self) -> InputObjectTypeExtensionNode:
+        """InputObjectTypeExtension"""
+        start = self._lexer.token
+        self.expect_keyword("extend")
+        self.expect_keyword("input")
+        name = self.parse_name()
+        directives = self.parse_directives(True)
+        fields = self.parse_input_fields_definition()
+        if not (directives or fields):
+            raise self.unexpected()
+        return InputObjectTypeExtensionNode(
+            name=name, directives=directives, fields=fields, loc=self.loc(start)
+        )
+
+    def parse_directive_definition(self) -> DirectiveDefinitionNode:
+        """DirectiveDefinition"""
+        start = self._lexer.token
+        description = self.parse_description()
+        self.expect_keyword("directive")
+        self.expect_token(TokenKind.AT)
+        name = self.parse_name()
+        args = self.parse_argument_defs()
+        repeatable = self.expect_optional_keyword("repeatable")
+        self.expect_keyword("on")
+        locations = self.parse_directive_locations()
+        return DirectiveDefinitionNode(
+            description=description,
+            name=name,
+            arguments=args,
+            repeatable=repeatable,
+            locations=locations,
+            loc=self.loc(start),
+        )
+
+    def parse_directive_locations(self) -> List[NameNode]:
+        """DirectiveLocations"""
+        # optional leading pipe
+        self.expect_optional_token(TokenKind.PIPE)
+        locations: List[NameNode] = []
+        append = locations.append
+        while True:
+            append(self.parse_directive_location())
+            if not self.expect_optional_token(TokenKind.PIPE):
+                break
+        return locations
+
+    def parse_directive_location(self) -> NameNode:
+        """DirectiveLocation"""
+        start = self._lexer.token
+        name = self.parse_name()
+        if name.value in DirectiveLocation.__members__:
+            return name
+        raise self.unexpected(start)
+
+    # Core parsing utility functions
+
+    def loc(self, start_token: Token) -> Optional[Location]:
+        """Return a location object.
+
+        Used to identify the place in the source that created a given parsed object.
+        """
+        if not self._no_location:
+            end_token = self._lexer.last_token
+            source = self._lexer.source
+            return Location(start_token, end_token, source)
+        return None
+
+    def peek(self, kind: TokenKind) -> bool:
+        """Determine if the next token is of a given kind"""
+        return self._lexer.token.kind == kind
+
+    def expect_token(self, kind: TokenKind) -> Token:
+        """Expect the next token to be of the given kind.
+
+        If the next token is of the given kind, return that token after advancing
+        the lexer. Otherwise, do not change the parser state and throw an error.
+        """
+        token = self._lexer.token
+        if token.kind == kind:
+            self._lexer.advance()
+            return token
+
+        raise GraphQLSyntaxError(
+            self._lexer.source,
+            token.start,
+            f"Expected {get_token_kind_desc(kind)}, found {get_token_desc(token)}.",
+        )
+
+    def expect_optional_token(self, kind: TokenKind) -> Optional[Token]:
+        """Expect the next token optionally to be of the given kind.
+
+        If the next token is of the given kind, return that token after advancing
+        the lexer. Otherwise, do not change the parser state and return None.
+        """
+        token = self._lexer.token
+        if token.kind == kind:
+            self._lexer.advance()
+            return token
+
+        return None
+
+    def expect_keyword(self, value: str) -> None:
+        """Expect the next token to be a given keyword.
+
+        If the next token is a given keyword, advance the lexer.
+        Otherwise, do not change the parser state and throw an error.
+        """
+        token = self._lexer.token
+        if token.kind == TokenKind.NAME and token.value == value:
+            self._lexer.advance()
+        else:
+            raise GraphQLSyntaxError(
+                self._lexer.source,
+                token.start,
+                f"Expected '{value}', found {get_token_desc(token)}.",
+            )
+
+    def expect_optional_keyword(self, value: str) -> bool:
+        """Expect the next token optionally to be a given keyword.
+
+        If the next token is a given keyword, return True after advancing the lexer.
+        Otherwise, do not change the parser state and return False.
+        """
+        token = self._lexer.token
+        if token.kind == TokenKind.NAME and token.value == value:
+            self._lexer.advance()
+            return True
+
+        return False
+
+    def unexpected(self, at_token: Optional[Token] = None) -> GraphQLError:
+        """Create an error when an unexpected lexed token is encountered."""
+        token = at_token or self._lexer.token
+        return GraphQLSyntaxError(
+            self._lexer.source, token.start, f"Unexpected {get_token_desc(token)}."
+        )
+
+    def any(
+        self, open_kind: TokenKind, parse_fn: Callable[[], T], close_kind: TokenKind
+    ) -> List[T]:
+        """Fetch any matching nodes, possibly none.
+
+        Returns a possibly empty list of parse nodes, determined by the ``parse_fn``.
+        This list begins with a lex token of ``open_kind`` and ends with a lex token of
+        ``close_kind``. Advances the parser to the next lex token after the closing
+        token.
+        """
+        self.expect_token(open_kind)
+        nodes: List[T] = []
+        append = nodes.append
+        while not self.expect_optional_token(close_kind):
+            append(parse_fn())
+        return nodes
+
+    def optional_many(
+        self, open_kind: TokenKind, parse_fn: Callable[[], T], close_kind: TokenKind
+    ) -> List[T]:
+        """Fetch matching nodes, maybe none.
+
+        Returns a list of parse nodes, determined by the ``parse_fn``. It can be empty
+        only if the open token is missing, otherwise it will always return a non-empty
+        list that begins with a lex token of ``open_kind`` and ends with a lex token of
+        ``close_kind``. Advances the parser to the next lex token after the closing
+        token.
+        """
+        if self.expect_optional_token(open_kind):
+            nodes = [parse_fn()]
+            append = nodes.append
+            while not self.expect_optional_token(close_kind):
+                append(parse_fn())
+            return nodes
+        return []
+
+    def many(
+        self, open_kind: TokenKind, parse_fn: Callable[[], T], close_kind: TokenKind
+    ) -> List[T]:
+        """Fetch matching nodes, at least one.
+
+        Returns a non-empty list of parse nodes, determined by the ``parse_fn``.
+        This list begins with a lex token of ``open_kind`` and ends with a lex token of
+        ``close_kind``. Advances the parser to the next lex token after the closing
+        token.
+        """
+        self.expect_token(open_kind)
+        nodes = [parse_fn()]
+        append = nodes.append
+        while not self.expect_optional_token(close_kind):
+            append(parse_fn())
+        return nodes
+
+
+def get_token_desc(token: Token) -> str:
+    """Describe a token as a string for debugging."""
+    value = token.value
+    return get_token_kind_desc(token.kind) + (
+        f" '{value}'" if value is not None else ""
+    )
+
+
+def get_token_kind_desc(kind: TokenKind) -> str:
+    """Describe a token kind as a string for debugging."""
+    return f"'{kind.value}'" if is_punctuator_token_kind(kind) else kind.value
diff --git a/src/graphql/language/predicates.py b/src/graphql/language/predicates.py
new file mode 100644
index 0000000..db3c855
--- /dev/null
+++ b/src/graphql/language/predicates.py
@@ -0,0 +1,60 @@
+from .ast import (
+    Node,
+    DefinitionNode,
+    ExecutableDefinitionNode,
+    SchemaExtensionNode,
+    SelectionNode,
+    TypeDefinitionNode,
+    TypeExtensionNode,
+    TypeNode,
+    TypeSystemDefinitionNode,
+    ValueNode,
+)
+
+__all__ = [
+    "is_definition_node",
+    "is_executable_definition_node",
+    "is_selection_node",
+    "is_value_node",
+    "is_type_node",
+    "is_type_system_definition_node",
+    "is_type_definition_node",
+    "is_type_system_extension_node",
+    "is_type_extension_node",
+]
+
+
+def is_definition_node(node: Node) -> bool:
+    return isinstance(node, DefinitionNode)
+
+
+def is_executable_definition_node(node: Node) -> bool:
+    return isinstance(node, ExecutableDefinitionNode)
+
+
+def is_selection_node(node: Node) -> bool:
+    return isinstance(node, SelectionNode)
+
+
+def is_value_node(node: Node) -> bool:
+    return isinstance(node, ValueNode)
+
+
+def is_type_node(node: Node) -> bool:
+    return isinstance(node, TypeNode)
+
+
+def is_type_system_definition_node(node: Node) -> bool:
+    return isinstance(node, TypeSystemDefinitionNode)
+
+
+def is_type_definition_node(node: Node) -> bool:
+    return isinstance(node, TypeDefinitionNode)
+
+
+def is_type_system_extension_node(node: Node) -> bool:
+    return isinstance(node, (SchemaExtensionNode, TypeExtensionNode))
+
+
+def is_type_extension_node(node: Node) -> bool:
+    return isinstance(node, TypeExtensionNode)
diff --git a/src/graphql/language/print_location.py b/src/graphql/language/print_location.py
new file mode 100644
index 0000000..82e5f74
--- /dev/null
+++ b/src/graphql/language/print_location.py
@@ -0,0 +1,80 @@
+import re
+from typing import Optional, Tuple, cast
+
+from .ast import Location
+from .location import SourceLocation, get_location
+from .source import Source
+
+
+__all__ = ["print_location", "print_source_location"]
+
+
+def print_location(location: Location) -> str:
+    """Render a helpful description of the location in the GraphQL Source document."""
+    return print_source_location(
+        location.source, get_location(location.source, location.start)
+    )
+
+
+_re_newline = re.compile(r"\r\n|[\n\r]")
+
+
+def print_source_location(source: Source, source_location: SourceLocation) -> str:
+    """Render a helpful description of the location in the GraphQL Source document."""
+    first_line_column_offset = source.location_offset.column - 1
+    body = " " * first_line_column_offset + source.body
+
+    line_index = source_location.line - 1
+    line_offset = source.location_offset.line - 1
+    line_num = source_location.line + line_offset
+
+    column_offset = first_line_column_offset if source_location.line == 1 else 0
+    column_num = source_location.column + column_offset
+    location_str = f"{source.name}:{line_num}:{column_num}\n"
+
+    lines = _re_newline.split(body)  # works a bit different from splitlines()
+    location_line = lines[line_index]
+
+    # Special case for minified documents
+    if len(location_line) > 120:
+        sub_line_index, sub_line_column_num = divmod(column_num, 80)
+        sub_lines = [
+            location_line[i : i + 80] for i in range(0, len(location_line), 80)
+        ]
+
+        return location_str + print_prefixed_lines(
+            (str(line_num), sub_lines[0]),
+            *[("", sub_line) for sub_line in sub_lines[1 : sub_line_index + 1]],
+            (" ", " " * (sub_line_column_num - 1) + "^"),
+            (
+                "",
+                sub_lines[sub_line_index + 1]
+                if sub_line_index < len(sub_lines) - 1
+                else None,
+            ),
+        )
+
+    return location_str + print_prefixed_lines(
+        (f"{line_num - 1}", lines[line_index - 1] if line_index > 0 else None),
+        (f"{line_num}", location_line),
+        ("", " " * (column_num - 1) + "^"),
+        (
+            f"{line_num + 1}",
+            lines[line_index + 1] if line_index < len(lines) - 1 else None,
+        ),
+    )
+
+
+def print_prefixed_lines(*lines: Tuple[str, Optional[str]]) -> str:
+    """Print lines specified like this: ("prefix", "string")"""
+    existing_lines = [
+        cast(Tuple[str, str], line) for line in lines if line[1] is not None
+    ]
+    pad_len = max(len(line[0]) for line in existing_lines)
+    return "\n".join(
+        map(
+            lambda line: line[0].rjust(pad_len)
+            + (" | " + line[1] if line[1] else " |"),
+            existing_lines,
+        )
+    )
diff --git a/src/graphql/language/printer.py b/src/graphql/language/printer.py
new file mode 100644
index 0000000..e709a79
--- /dev/null
+++ b/src/graphql/language/printer.py
@@ -0,0 +1,430 @@
+from functools import wraps
+from json import dumps
+from typing import Any, Callable, Collection, Optional
+
+from ..language.ast import Node, OperationType
+from .visitor import visit, Visitor
+from .block_string import print_block_string
+
+__all__ = ["print_ast"]
+
+
+Strings = Collection[str]
+
+
+class PrintedNode:
+    """A union type for all nodes that have been processed by the printer."""
+
+    alias: str
+    arguments: Strings
+    block: bool
+    default_value: str
+    definitions: Strings
+    description: str
+    directives: str
+    fields: Strings
+    interfaces: Strings
+    locations: Strings
+    name: str
+    operation: OperationType
+    operation_types: Strings
+    repeatable: bool
+    selection_set: str
+    selections: Strings
+    type: str
+    type_condition: str
+    types: Strings
+    value: str
+    values: Strings
+    variable: str
+    variable_definitions: Strings
+
+
+def print_ast(ast: Node) -> str:
+    """Convert an AST into a string.
+
+    The conversion is done using a set of reasonable formatting rules.
+    """
+    return visit(ast, PrintAstVisitor())
+
+
+def add_description(method: Callable[..., str]) -> Callable:
+    """Decorator adding the description to the output of a static visitor method."""
+
+    @wraps(method)
+    def wrapped(node: PrintedNode, *args: Any) -> str:
+        return join((node.description, method(node, *args)), "\n")
+
+    return wrapped
+
+
+class PrintAstVisitor(Visitor):
+    @staticmethod
+    def leave_name(node: PrintedNode, *_args: Any) -> str:
+        return node.value
+
+    @staticmethod
+    def leave_variable(node: PrintedNode, *_args: Any) -> str:
+        return f"${node.name}"
+
+    # Document
+
+    @staticmethod
+    def leave_document(node: PrintedNode, *_args: Any) -> str:
+        return join(node.definitions, "\n\n") + "\n"
+
+    @staticmethod
+    def leave_operation_definition(node: PrintedNode, *_args: Any) -> str:
+        name, op, selection_set = node.name, node.operation, node.selection_set
+        var_defs = wrap("(", join(node.variable_definitions, ", "), ")")
+        directives = join(node.directives, " ")
+        # Anonymous queries with no directives or variable definitions can use the
+        # query short form.
+        return (
+            join((op.value, join((name, var_defs)), directives, selection_set), " ")
+            if (name or directives or var_defs or op != OperationType.QUERY)
+            else selection_set
+        )
+
+    @staticmethod
+    def leave_variable_definition(node: PrintedNode, *_args: Any) -> str:
+        return (
+            f"{node.variable}: {node.type}"
+            f"{wrap(' = ', node.default_value)}"
+            f"{wrap(' ', join(node.directives, ' '))}"
+        )
+
+    @staticmethod
+    def leave_selection_set(node: PrintedNode, *_args: Any) -> str:
+        return block(node.selections)
+
+    @staticmethod
+    def leave_field(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                wrap("", node.alias, ": ")
+                + node.name
+                + wrap("(", join(node.arguments, ", "), ")"),
+                join(node.directives, " "),
+                node.selection_set,
+            ),
+            " ",
+        )
+
+    @staticmethod
+    def leave_argument(node: PrintedNode, *_args: Any) -> str:
+        return f"{node.name}: {node.value}"
+
+    # Fragments
+
+    @staticmethod
+    def leave_fragment_spread(node: PrintedNode, *_args: Any) -> str:
+        return f"...{node.name}{wrap(' ', join(node.directives, ' '))}"
+
+    @staticmethod
+    def leave_inline_fragment(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                "...",
+                wrap("on ", node.type_condition),
+                join(node.directives, " "),
+                node.selection_set,
+            ),
+            " ",
+        )
+
+    @staticmethod
+    def leave_fragment_definition(node: PrintedNode, *_args: Any) -> str:
+        # Note: fragment variable definitions are experimental and may be changed or
+        # removed in the future.
+        return (
+            f"fragment {node.name}"
+            f"{wrap('(', join(node.variable_definitions, ', '), ')')}"
+            f" on {node.type_condition}"
+            f" {wrap('', join(node.directives, ' '), ' ')}"
+            f"{node.selection_set}"
+        )
+
+    # Value
+
+    @staticmethod
+    def leave_int_value(node: PrintedNode, *_args: Any) -> str:
+        return node.value
+
+    @staticmethod
+    def leave_float_value(node: PrintedNode, *_args: Any) -> str:
+        return node.value
+
+    @staticmethod
+    def leave_string_value(node: PrintedNode, key: str, *_args: Any) -> str:
+        if node.block:
+            return print_block_string(node.value, "" if key == "description" else "  ")
+        return dumps(node.value)
+
+    @staticmethod
+    def leave_boolean_value(node: PrintedNode, *_args: Any) -> str:
+        return "true" if node.value else "false"
+
+    @staticmethod
+    def leave_null_value(_node: PrintedNode, *_args: Any) -> str:
+        return "null"
+
+    @staticmethod
+    def leave_enum_value(node: PrintedNode, *_args: Any) -> str:
+        return node.value
+
+    @staticmethod
+    def leave_list_value(node: PrintedNode, *_args: Any) -> str:
+        return f"[{join(node.values, ', ')}]"
+
+    @staticmethod
+    def leave_object_value(node: PrintedNode, *_args: Any) -> str:
+        return f"{{{join(node.fields, ', ')}}}"
+
+    @staticmethod
+    def leave_object_field(node: PrintedNode, *_args: Any) -> str:
+        return f"{node.name}: {node.value}"
+
+    # Directive
+
+    @staticmethod
+    def leave_directive(node: PrintedNode, *_args: Any) -> str:
+        return f"@{node.name}{wrap('(', join(node.arguments, ', '), ')')}"
+
+    # Type
+
+    @staticmethod
+    def leave_named_type(node: PrintedNode, *_args: Any) -> str:
+        return node.name
+
+    @staticmethod
+    def leave_list_type(node: PrintedNode, *_args: Any) -> str:
+        return f"[{node.type}]"
+
+    @staticmethod
+    def leave_non_null_type(node: PrintedNode, *_args: Any) -> str:
+        return f"{node.type}!"
+
+    # Type System Definitions
+
+    @staticmethod
+    @add_description
+    def leave_schema_definition(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            ("schema", join(node.directives, " "), block(node.operation_types)), " "
+        )
+
+    @staticmethod
+    def leave_operation_type_definition(node: PrintedNode, *_args: Any) -> str:
+        return f"{node.operation.value}: {node.type}"
+
+    @staticmethod
+    @add_description
+    def leave_scalar_type_definition(node: PrintedNode, *_args: Any) -> str:
+        return join(("scalar", node.name, join(node.directives, " ")), " ")
+
+    @staticmethod
+    @add_description
+    def leave_object_type_definition(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                "type",
+                node.name,
+                wrap("implements ", join(node.interfaces, " & ")),
+                join(node.directives, " "),
+                block(node.fields),
+            ),
+            " ",
+        )
+
+    @staticmethod
+    @add_description
+    def leave_field_definition(node: PrintedNode, *_args: Any) -> str:
+        args = node.arguments
+        args = (
+            wrap("(\n", indent(join(args, "\n")), "\n)")
+            if has_multiline_items(args)
+            else wrap("(", join(args, ", "), ")")
+        )
+        directives = wrap(" ", join(node.directives, " "))
+        return f"{node.name}{args}: {node.type}{directives}"
+
+    @staticmethod
+    @add_description
+    def leave_input_value_definition(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                f"{node.name}: {node.type}",
+                wrap("= ", node.default_value),
+                join(node.directives, " "),
+            ),
+            " ",
+        )
+
+    @staticmethod
+    @add_description
+    def leave_interface_type_definition(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                "interface",
+                node.name,
+                wrap("implements ", join(node.interfaces, " & ")),
+                join(node.directives, " "),
+                block(node.fields),
+            ),
+            " ",
+        )
+
+    @staticmethod
+    @add_description
+    def leave_union_type_definition(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                "union",
+                node.name,
+                join(node.directives, " "),
+                "= " + join(node.types, " | ") if node.types else "",
+            ),
+            " ",
+        )
+
+    @staticmethod
+    @add_description
+    def leave_enum_type_definition(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            ("enum", node.name, join(node.directives, " "), block(node.values)), " "
+        )
+
+    @staticmethod
+    @add_description
+    def leave_enum_value_definition(node: PrintedNode, *_args: Any) -> str:
+        return join((node.name, join(node.directives, " ")), " ")
+
+    @staticmethod
+    @add_description
+    def leave_input_object_type_definition(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            ("input", node.name, join(node.directives, " "), block(node.fields)), " "
+        )
+
+    @staticmethod
+    @add_description
+    def leave_directive_definition(node: PrintedNode, *_args: Any) -> str:
+        args = node.arguments
+        args = (
+            wrap("(\n", indent(join(args, "\n")), "\n)")
+            if has_multiline_items(args)
+            else wrap("(", join(args, ", "), ")")
+        )
+        repeatable = " repeatable" if node.repeatable else ""
+        locations = join(node.locations, " | ")
+        return f"directive @{node.name}{args}{repeatable} on {locations}"
+
+    @staticmethod
+    def leave_schema_extension(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            ("extend schema", join(node.directives, " "), block(node.operation_types)),
+            " ",
+        )
+
+    @staticmethod
+    def leave_scalar_type_extension(node: PrintedNode, *_args: Any) -> str:
+        return join(("extend scalar", node.name, join(node.directives, " ")), " ")
+
+    @staticmethod
+    def leave_object_type_extension(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                "extend type",
+                node.name,
+                wrap("implements ", join(node.interfaces, " & ")),
+                join(node.directives, " "),
+                block(node.fields),
+            ),
+            " ",
+        )
+
+    @staticmethod
+    def leave_interface_type_extension(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                "extend interface",
+                node.name,
+                wrap("implements ", join(node.interfaces, " & ")),
+                join(node.directives, " "),
+                block(node.fields),
+            ),
+            " ",
+        )
+
+    @staticmethod
+    def leave_union_type_extension(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            (
+                "extend union",
+                node.name,
+                join(node.directives, " "),
+                "= " + join(node.types, " | ") if node.types else "",
+            ),
+            " ",
+        )
+
+    @staticmethod
+    def leave_enum_type_extension(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            ("extend enum", node.name, join(node.directives, " "), block(node.values)),
+            " ",
+        )
+
+    @staticmethod
+    def leave_input_object_type_extension(node: PrintedNode, *_args: Any) -> str:
+        return join(
+            ("extend input", node.name, join(node.directives, " "), block(node.fields)),
+            " ",
+        )
+
+
+def join(strings: Optional[Strings], separator: str = "") -> str:
+    """Join strings in a given collection.
+
+    Return an empty string if it is None or empty, otherwise join all items together
+    separated by separator if provided.
+    """
+    return separator.join(s for s in strings if s) if strings else ""
+
+
+def block(strings: Strings) -> str:
+    """Return strings inside a block.
+
+    Given a collection of strings, return a string with each item on its own line,
+    wrapped in an indented "{ }" block.
+    """
+    return "{\n" + indent(join(strings, "\n")) + "\n}" if strings else ""
+
+
+def wrap(start: str, string: str, end: str = "") -> str:
+    """Wrap string inside other strings at start and end.
+
+    If the string is not None or empty, then wrap with start and end, otherwise return
+    an empty string.
+    """
+    return f"{start}{string}{end}" if string else ""
+
+
+def indent(string: str) -> str:
+    """Indent string with two spaces.
+
+    If the string is not None or empty, add two spaces at the beginning of every line
+    inside the string.
+    """
+    return "  " + string.replace("\n", "\n  ") if string else string
+
+
+def is_multiline(string: str) -> bool:
+    """Check whether a string consists of multiple lines."""
+    return "\n" in string
+
+
+def has_multiline_items(maybe_list: Optional[Strings]) -> bool:
+    """Check whether one of the items in the list has multiple lines."""
+    return any(is_multiline(item) for item in maybe_list) if maybe_list else False
diff --git a/src/graphql/language/source.py b/src/graphql/language/source.py
new file mode 100644
index 0000000..1d193a4
--- /dev/null
+++ b/src/graphql/language/source.py
@@ -0,0 +1,67 @@
+from typing import Any
+
+from .location import SourceLocation
+
+__all__ = ["Source"]
+
+
+class Source:
+    """A representation of source input to GraphQL."""
+
+    # allow custom attributes and weak references (not used internally)
+    __slots__ = "__weakref__", "__dict__", "body", "name", "location_offset"
+
+    def __init__(
+        self,
+        body: str,
+        name: str = "GraphQL request",
+        location_offset: SourceLocation = SourceLocation(1, 1),
+    ) -> None:
+        """Initialize source input.
+
+
+        ``name`` and ``location_offset`` are optional. They are useful for clients who
+        store GraphQL documents in source files; for example, if the GraphQL input
+        starts at line 40 in a file named Foo.graphql, it might be useful for name
+        to be "Foo.graphql" and location to be ``(40, 0)``.
+
+        line and column in location_offset are 1-indexed
+        """
+        if not isinstance(body, str):
+            raise TypeError("body must be a string.")
+        self.body = body
+        if not isinstance(name, str):
+            raise TypeError("name must be a string.")
+        self.name = name
+        if not isinstance(location_offset, SourceLocation):
+            location_offset = SourceLocation._make(location_offset)
+        if location_offset.line <= 0:
+            raise ValueError(
+                "line in location_offset is 1-indexed and must be positive."
+            )
+        if location_offset.column <= 0:
+            raise ValueError(
+                "column in location_offset is 1-indexed and must be positive."
+            )
+        self.location_offset = location_offset
+
+    def get_location(self, position: int) -> SourceLocation:
+        lines = self.body[:position].splitlines()
+        if lines:
+            line = len(lines)
+            column = len(lines[-1]) + 1
+        else:
+            line = 1
+            column = 1
+        return SourceLocation(line, column)
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__} name={self.name!r}>"
+
+    def __eq__(self, other: Any) -> bool:
+        return (isinstance(other, Source) and other.body == self.body) or (
+            isinstance(other, str) and other == self.body
+        )
+
+    def __ne__(self, other: Any) -> bool:
+        return not self == other
diff --git a/src/graphql/language/token_kind.py b/src/graphql/language/token_kind.py
new file mode 100644
index 0000000..45f6e82
--- /dev/null
+++ b/src/graphql/language/token_kind.py
@@ -0,0 +1,30 @@
+from enum import Enum
+
+__all__ = ["TokenKind"]
+
+
+class TokenKind(Enum):
+    """The different kinds of tokens that the lexer emits"""
+
+    SOF = "<SOF>"
+    EOF = "<EOF>"
+    BANG = "!"
+    DOLLAR = "$"
+    AMP = "&"
+    PAREN_L = "("
+    PAREN_R = ")"
+    SPREAD = "..."
+    COLON = ":"
+    EQUALS = "="
+    AT = "@"
+    BRACKET_L = "["
+    BRACKET_R = "]"
+    BRACE_L = "{"
+    PIPE = "|"
+    BRACE_R = "}"
+    NAME = "Name"
+    INT = "Int"
+    FLOAT = "Float"
+    STRING = "String"
+    BLOCK_STRING = "BlockString"
+    COMMENT = "Comment"
diff --git a/src/graphql/language/visitor.py b/src/graphql/language/visitor.py
new file mode 100644
index 0000000..2378e27
--- /dev/null
+++ b/src/graphql/language/visitor.py
@@ -0,0 +1,405 @@
+from copy import copy
+from enum import Enum
+from typing import (
+    Any,
+    Callable,
+    Collection,
+    Dict,
+    List,
+    NamedTuple,
+    Optional,
+    Tuple,
+    Union,
+)
+
+from ..pyutils import inspect, snake_to_camel
+from . import ast
+
+from .ast import Node
+
+__all__ = [
+    "Visitor",
+    "ParallelVisitor",
+    "VisitorAction",
+    "visit",
+    "BREAK",
+    "SKIP",
+    "REMOVE",
+    "IDLE",
+    "QUERY_DOCUMENT_KEYS",
+]
+
+
+class VisitorActionEnum(Enum):
+    """Special return values for the visitor methods.
+
+    You can also use the values of this enum directly.
+    """
+
+    BREAK = True
+    SKIP = False
+    REMOVE = Ellipsis
+
+
+VisitorAction = Optional[VisitorActionEnum]
+
+# Note that in GraphQL.js these are defined differently:
+# BREAK = {}, SKIP = false, REMOVE = null, IDLE = undefined
+
+BREAK = VisitorActionEnum.BREAK
+SKIP = VisitorActionEnum.SKIP
+REMOVE = VisitorActionEnum.REMOVE
+IDLE = None
+
+# Default map from visitor kinds to their traversable node attributes:
+QUERY_DOCUMENT_KEYS: Dict[str, Tuple[str, ...]] = {
+    "name": (),
+    "document": ("definitions",),
+    "operation_definition": (
+        "name",
+        "variable_definitions",
+        "directives",
+        "selection_set",
+    ),
+    "variable_definition": ("variable", "type", "default_value", "directives"),
+    "variable": ("name",),
+    "selection_set": ("selections",),
+    "field": ("alias", "name", "arguments", "directives", "selection_set"),
+    "argument": ("name", "value"),
+    "fragment_spread": ("name", "directives"),
+    "inline_fragment": ("type_condition", "directives", "selection_set"),
+    "fragment_definition": (
+        # Note: fragment variable definitions are experimental and may be changed or
+        # removed in the future.
+        "name",
+        "variable_definitions",
+        "type_condition",
+        "directives",
+        "selection_set",
+    ),
+    "int_value": (),
+    "float_value": (),
+    "string_value": (),
+    "boolean_value": (),
+    "enum_value": (),
+    "list_value": ("values",),
+    "object_value": ("fields",),
+    "object_field": ("name", "value"),
+    "directive": ("name", "arguments"),
+    "named_type": ("name",),
+    "list_type": ("type",),
+    "non_null_type": ("type",),
+    "schema_definition": ("description", "directives", "operation_types"),
+    "operation_type_definition": ("type",),
+    "scalar_type_definition": ("description", "name", "directives"),
+    "object_type_definition": (
+        "description",
+        "name",
+        "interfaces",
+        "directives",
+        "fields",
+    ),
+    "field_definition": ("description", "name", "arguments", "type", "directives"),
+    "input_value_definition": (
+        "description",
+        "name",
+        "type",
+        "default_value",
+        "directives",
+    ),
+    "interface_type_definition": (
+        "description",
+        "name",
+        "interfaces",
+        "directives",
+        "fields",
+    ),
+    "union_type_definition": ("description", "name", "directives", "types"),
+    "enum_type_definition": ("description", "name", "directives", "values"),
+    "enum_value_definition": ("description", "name", "directives"),
+    "input_object_type_definition": ("description", "name", "directives", "fields"),
+    "directive_definition": ("description", "name", "arguments", "locations"),
+    "schema_extension": ("directives", "operation_types"),
+    "scalar_type_extension": ("name", "directives"),
+    "object_type_extension": ("name", "interfaces", "directives", "fields"),
+    "interface_type_extension": ("name", "interfaces", "directives", "fields"),
+    "union_type_extension": ("name", "directives", "types"),
+    "enum_type_extension": ("name", "directives", "values"),
+    "input_object_type_extension": ("name", "directives", "fields"),
+}
+
+
+class Visitor:
+    """Visitor that walks through an AST.
+
+    Visitors can define two generic methods "enter" and "leave". The former will be
+    called when a node is entered in the traversal, the latter is called after visiting
+    the node and its child nodes. These methods have the following signature::
+
+        def enter(self, node, key, parent, path, ancestors):
+            # The return value has the following meaning:
+            # IDLE (None): no action
+            # SKIP: skip visiting this node
+            # BREAK: stop visiting altogether
+            # REMOVE: delete this node
+            # any other value: replace this node with the returned value
+            return
+
+        def leave(self, node, key, parent, path, ancestors):
+            # The return value has the following meaning:
+            # IDLE (None) or SKIP: no action
+            # BREAK: stop visiting altogether
+            # REMOVE: delete this node
+            # any other value: replace this node with the returned value
+            return
+
+    The parameters have the following meaning:
+
+    :arg node: The current node being visiting.
+    :arg key: The index or key to this node from the parent node or Array.
+    :arg parent: the parent immediately above this node, which may be an Array.
+    :arg path: The key path to get to this node from the root node.
+    :arg ancestors: All nodes and Arrays visited before reaching parent
+        of this node. These correspond to array indices in ``path``.
+        Note: ancestors includes arrays which contain the parent of visited node.
+
+    You can also define node kind specific methods by suffixing them with an underscore
+    followed by the kind of the node to be visited. For instance, to visit ``field``
+    nodes, you would defined the methods ``enter_field()`` and/or ``leave_field()``,
+    with the same signature as above. If no kind specific method has been defined
+    for a given node, the generic method is called.
+    """
+
+    # Provide special return values as attributes
+    BREAK, SKIP, REMOVE, IDLE = BREAK, SKIP, REMOVE, IDLE
+
+    def __init_subclass__(cls) -> None:
+        """Verify that all defined handlers are valid."""
+        super().__init_subclass__()
+        for attr, val in cls.__dict__.items():
+            if attr.startswith("_"):
+                continue
+            attr_kind = attr.split("_", 1)
+            if len(attr_kind) < 2:
+                kind: Optional[str] = None
+            else:
+                attr, kind = attr_kind
+            if attr in ("enter", "leave"):
+                if kind:
+                    name = snake_to_camel(kind) + "Node"
+                    node_cls = getattr(ast, name, None)
+                    if (
+                        not node_cls
+                        or not isinstance(node_cls, type)
+                        or not issubclass(node_cls, Node)
+                    ):
+                        raise TypeError(f"Invalid AST node kind: {kind}.")
+
+    def get_visit_fn(self, kind: str, is_leaving: bool = False) -> Callable:
+        """Get the visit function for the given node kind and direction."""
+        method = "leave" if is_leaving else "enter"
+        visit_fn = getattr(self, f"{method}_{kind}", None)
+        if not visit_fn:
+            visit_fn = getattr(self, method, None)
+        return visit_fn
+
+
+class Stack(NamedTuple):
+    """A stack for the visit function."""
+
+    in_array: bool
+    idx: int
+    keys: Tuple[Node, ...]
+    edits: List[Tuple[Union[int, str], Node]]
+    prev: Any  # 'Stack' (python/mypy/issues/731)
+
+
+def visit(
+    root: Node,
+    visitor: Visitor,
+    visitor_keys: Optional[Dict[str, Tuple[str, ...]]] = None,
+) -> Any:
+    """Visit each node in an AST.
+
+    :func:`~.visit` will walk through an AST using a depth first traversal, calling the
+    visitor's enter methods at each node in the traversal, and calling the leave methods
+    after visiting that node and all of its child nodes.
+
+    By returning different values from the enter and leave methods, the behavior of the
+    visitor can be altered, including skipping over a sub-tree of the AST (by returning
+    False), editing the AST by returning a value or None to remove the value, or to stop
+    the whole traversal by returning :data:`~.BREAK`.
+
+    When using :func:`~.visit` to edit an AST, the original AST will not be modified,
+    and a new version of the AST with the changes applied will be returned from the
+    visit function.
+
+    To customize the node attributes to be used for traversal, you can provide a
+    dictionary visitor_keys mapping node kinds to node attributes.
+    """
+    if not isinstance(root, Node):
+        raise TypeError(f"Not an AST Node: {inspect(root)}.")
+    if not isinstance(visitor, Visitor):
+        raise TypeError(f"Not an AST Visitor: {inspect(visitor)}.")
+    if visitor_keys is None:
+        visitor_keys = QUERY_DOCUMENT_KEYS
+    stack: Any = None
+    in_array = isinstance(root, list)
+    keys: Tuple[Node, ...] = (root,)
+    idx = -1
+    edits: List[Any] = []
+    parent: Any = None
+    path: List[Any] = []
+    path_append = path.append
+    path_pop = path.pop
+    ancestors: List[Any] = []
+    ancestors_append = ancestors.append
+    ancestors_pop = ancestors.pop
+    new_root = root
+
+    while True:
+        idx += 1
+        is_leaving = idx == len(keys)
+        is_edited = is_leaving and edits
+        if is_leaving:
+            key = path[-1] if ancestors else None
+            node: Any = parent
+            parent = ancestors_pop() if ancestors else None
+            if is_edited:
+                if in_array:
+                    node = node[:]
+                else:
+                    node = copy(node)
+            edit_offset = 0
+            for edit_key, edit_value in edits:
+                if in_array:
+                    edit_key -= edit_offset
+                if in_array and (edit_value is REMOVE or edit_value is Ellipsis):
+                    node.pop(edit_key)
+                    edit_offset += 1
+                else:
+                    if isinstance(node, list):
+                        node[edit_key] = edit_value
+                    else:
+                        setattr(node, edit_key, edit_value)
+
+            idx = stack.idx
+            keys = stack.keys
+            edits = stack.edits
+            in_array = stack.in_array
+            stack = stack.prev
+        else:
+            if parent:
+                if in_array:
+                    key = idx
+                    node = parent[key]
+                else:
+                    key = keys[idx]
+                    node = getattr(parent, key, None)
+            else:
+                key = None
+                node = new_root
+            if node is None:
+                continue
+            if parent:
+                path_append(key)
+
+        if isinstance(node, list):
+            result = None
+        else:
+            if not isinstance(node, Node):
+                raise TypeError(f"Invalid AST Node: {inspect(node)}.")
+            visit_fn = visitor.get_visit_fn(node.kind, is_leaving)
+            if visit_fn:
+                result = visit_fn(node, key, parent, path, ancestors)
+
+                if result is BREAK or result is True:
+                    break
+
+                if result is SKIP or result is False:
+                    if not is_leaving:
+                        path_pop()
+                        continue
+
+                elif result is not None:
+                    edits.append((key, result))
+                    if not is_leaving:
+                        if isinstance(result, Node):
+                            node = result
+                        else:
+                            path_pop()
+                            continue
+            else:
+                result = None
+
+        if result is None and is_edited:
+            edits.append((key, node))
+
+        if is_leaving:
+            if path:
+                path_pop()
+        else:
+            stack = Stack(in_array, idx, keys, edits, stack)
+            in_array = isinstance(node, list)
+            keys = node if in_array else visitor_keys.get(node.kind, ())
+            idx = -1
+            edits = []
+            if parent:
+                ancestors_append(parent)
+            parent = node
+
+        if not stack:
+            break
+
+    if edits:
+        new_root = edits[-1][1]
+
+    return new_root
+
+
+class ParallelVisitor(Visitor):
+    """A Visitor which delegates to many visitors to run in parallel.
+
+    Each visitor will be visited for each node before moving on.
+
+    If a prior visitor edits a node, no following visitors will see that node.
+    """
+
+    def __init__(self, visitors: Collection[Visitor]):
+        """Create a new visitor from the given list of parallel visitors."""
+        self.visitors = visitors
+        self.skipping: List[Any] = [None] * len(visitors)
+
+    def enter(self, node: Node, *args: Any) -> Optional[VisitorAction]:
+        skipping = self.skipping
+        for i, visitor in enumerate(self.visitors):
+            if not skipping[i]:
+                fn = visitor.get_visit_fn(node.kind)
+                if fn:
+                    result = fn(node, *args)
+                    if result is SKIP or result is False:
+                        skipping[i] = node
+                    elif result is BREAK or result is True:
+                        skipping[i] = BREAK
+                    elif result is not None:
+                        return result
+        return None
+
+    def leave(self, node: Node, *args: Any) -> Optional[VisitorAction]:
+        skipping = self.skipping
+        for i, visitor in enumerate(self.visitors):
+            if not skipping[i]:
+                fn = visitor.get_visit_fn(node.kind, is_leaving=True)
+                if fn:
+                    result = fn(node, *args)
+                    if result is BREAK or result is True:
+                        skipping[i] = BREAK
+                    elif (
+                        result is not None
+                        and result is not SKIP
+                        and result is not False
+                    ):
+                        return result
+            elif skipping[i] is node:
+                skipping[i] = None
+        return None
diff --git a/src/graphql/py.typed b/src/graphql/py.typed
new file mode 100644
index 0000000..eb0b539
--- /dev/null
+++ b/src/graphql/py.typed
@@ -0,0 +1 @@
+# Marker file for PEP 561. The graphql package uses inline types.
diff --git a/src/graphql/pyutils/__init__.py b/src/graphql/pyutils/__init__.py
new file mode 100644
index 0000000..7b2492b
--- /dev/null
+++ b/src/graphql/pyutils/__init__.py
@@ -0,0 +1,62 @@
+"""Python Utils
+
+This package contains dependency-free Python utility functions used throughout the
+codebase.
+
+Each utility should belong in its own file and be the default export.
+
+These functions are not part of the module interface and are subject to change.
+"""
+
+from .convert_case import camel_to_snake, snake_to_camel
+from .cached_property import cached_property
+from .description import (
+    Description,
+    is_description,
+    register_description,
+    unregister_description,
+)
+from .did_you_mean import did_you_mean
+from .event_emitter import EventEmitter, EventEmitterAsyncIterator
+from .identity_func import identity_func
+from .inspect import inspect
+from .is_awaitable import is_awaitable
+from .is_collection import is_collection
+from .is_finite import is_finite
+from .is_integer import is_integer
+from .awaitable_or_value import AwaitableOrValue
+from .suggestion_list import suggestion_list
+from .frozen_error import FrozenError
+from .frozen_list import FrozenList
+from .frozen_dict import FrozenDict
+from .path import Path
+from .print_path_list import print_path_list
+from .undefined import Undefined, UndefinedType
+
+__all__ = [
+    "camel_to_snake",
+    "snake_to_camel",
+    "cached_property",
+    "did_you_mean",
+    "Description",
+    "is_description",
+    "register_description",
+    "unregister_description",
+    "EventEmitter",
+    "EventEmitterAsyncIterator",
+    "identity_func",
+    "inspect",
+    "is_awaitable",
+    "is_collection",
+    "is_finite",
+    "is_integer",
+    "AwaitableOrValue",
+    "suggestion_list",
+    "FrozenError",
+    "FrozenList",
+    "FrozenDict",
+    "Path",
+    "print_path_list",
+    "Undefined",
+    "UndefinedType",
+]
diff --git a/src/graphql/pyutils/awaitable_or_value.py b/src/graphql/pyutils/awaitable_or_value.py
new file mode 100644
index 0000000..b497a78
--- /dev/null
+++ b/src/graphql/pyutils/awaitable_or_value.py
@@ -0,0 +1,8 @@
+from typing import Awaitable, TypeVar, Union
+
+__all__ = ["AwaitableOrValue"]
+
+
+T = TypeVar("T")
+
+AwaitableOrValue = Union[Awaitable[T], T]
diff --git a/src/graphql/pyutils/cached_property.py b/src/graphql/pyutils/cached_property.py
new file mode 100644
index 0000000..ddb3372
--- /dev/null
+++ b/src/graphql/pyutils/cached_property.py
@@ -0,0 +1,35 @@
+from typing import Any, Callable, TYPE_CHECKING
+
+if TYPE_CHECKING:
+    standard_cached_property = None
+else:
+    try:
+        from functools import cached_property as standard_cached_property
+    except ImportError:  # Python < 3.8
+        standard_cached_property = None
+
+if standard_cached_property:
+    cached_property = standard_cached_property
+else:
+    # Code taken from https://github.com/bottlepy/bottle
+
+    class CachedProperty:
+        """A cached property.
+
+        A property that is only computed once per instance and then replaces itself with
+        an ordinary attribute. Deleting the attribute resets the property.
+        """
+
+        def __init__(self, func: Callable) -> None:
+            self.__doc__ = getattr(func, "__doc__")
+            self.func = func
+
+        def __get__(self, obj: object, cls: type) -> Any:
+            if obj is None:
+                return self
+            value = obj.__dict__[self.func.__name__] = self.func(obj)
+            return value
+
+    cached_property = CachedProperty
+
+__all__ = ["cached_property"]
diff --git a/src/graphql/pyutils/convert_case.py b/src/graphql/pyutils/convert_case.py
new file mode 100644
index 0000000..e78d0e7
--- /dev/null
+++ b/src/graphql/pyutils/convert_case.py
@@ -0,0 +1,25 @@
+# uses code from https://github.com/daveoncode/python-string-utils
+
+import re
+
+__all__ = ["camel_to_snake", "snake_to_camel"]
+
+_re_camel_to_snake = re.compile(r"([a-z]|[A-Z]+)(?=[A-Z])")
+_re_snake_to_camel = re.compile(r"(_)([a-z\d])")
+
+
+def camel_to_snake(s: str) -> str:
+    """Convert from CamelCase to snake_case"""
+    return _re_camel_to_snake.sub(r"\1_", s).lower()
+
+
+def snake_to_camel(s: str, upper: bool = True) -> str:
+    """Convert from snake_case to CamelCase
+
+    If upper is set, then convert to upper CamelCase, otherwise the first character
+    keeps its case.
+    """
+    s = _re_snake_to_camel.sub(lambda m: m.group(2).upper(), s)
+    if upper:
+        s = s[:1].upper() + s[1:]
+    return s
diff --git a/src/graphql/pyutils/description.py b/src/graphql/pyutils/description.py
new file mode 100644
index 0000000..ccb858e
--- /dev/null
+++ b/src/graphql/pyutils/description.py
@@ -0,0 +1,59 @@
+from typing import Any, Tuple, Union
+
+__all__ = [
+    "Description",
+    "is_description",
+    "register_description",
+    "unregister_description",
+]
+
+
+class Description:
+    """Type checker for human readable descriptions.
+
+    By default, only ordinary strings are accepted as descriptions,
+    but you can register() other classes that will also be allowed,
+    e.g. to support lazy string objects that are evaluated only at runtime.
+    If you register(object), any object will be allowed as description.
+    """
+
+    bases: Union[type, Tuple[type, ...]] = str
+
+    @classmethod
+    def isinstance(cls, obj: Any) -> bool:
+        return isinstance(obj, cls.bases)
+
+    @classmethod
+    def register(cls, base: type) -> None:
+        """Register a class that shall be accepted as a description."""
+        if not isinstance(base, type):
+            raise TypeError("Only types can be registered.")
+        if base is object:
+            cls.bases = object
+        elif cls.bases is object:
+            cls.bases = base
+        elif not isinstance(cls.bases, tuple):
+            if base is not cls.bases:
+                cls.bases = (cls.bases, base)
+        elif base not in cls.bases:
+            cls.bases += (base,)
+
+    @classmethod
+    def unregister(cls, base: type) -> None:
+        """Unregister a class that shall no more be accepted as a description."""
+        if not isinstance(base, type):
+            raise TypeError("Only types can be unregistered.")
+        if isinstance(cls.bases, tuple):
+            if base in cls.bases:
+                cls.bases = tuple(b for b in cls.bases if b is not base)
+            if not cls.bases:
+                cls.bases = object
+            elif len(cls.bases) == 1:
+                cls.bases = cls.bases[0]
+        elif cls.bases is base:
+            cls.bases = object
+
+
+is_description = Description.isinstance
+register_description = Description.register
+unregister_description = Description.unregister
diff --git a/src/graphql/pyutils/did_you_mean.py b/src/graphql/pyutils/did_you_mean.py
new file mode 100644
index 0000000..88ae620
--- /dev/null
+++ b/src/graphql/pyutils/did_you_mean.py
@@ -0,0 +1,28 @@
+from typing import Optional, Sequence
+
+__all__ = ["did_you_mean"]
+
+MAX_LENGTH = 5
+
+
+def did_you_mean(suggestions: Sequence[str], sub_message: Optional[str] = None) -> str:
+    """Given [ A, B, C ] return ' Did you mean A, B, or C?'"""
+    if not suggestions or not MAX_LENGTH:
+        return ""
+    parts = [" Did you mean "]
+    if sub_message:
+        parts.extend([sub_message, " "])
+    suggestions = suggestions[:MAX_LENGTH]
+    n = len(suggestions)
+    if n == 1:
+        parts.append(f"'{suggestions[0]}'?")
+    elif n == 2:
+        parts.append(f"'{suggestions[0]}' or '{suggestions[1]}'?")
+    else:
+        parts.extend(
+            [
+                ", ".join(f"'{s}'" for s in suggestions[:-1]),
+                f", or '{suggestions[-1]}'?",
+            ]
+        )
+    return "".join(parts)
diff --git a/src/graphql/pyutils/event_emitter.py b/src/graphql/pyutils/event_emitter.py
new file mode 100644
index 0000000..8ef103e
--- /dev/null
+++ b/src/graphql/pyutils/event_emitter.py
@@ -0,0 +1,64 @@
+from asyncio import AbstractEventLoop, Queue, ensure_future
+from collections import defaultdict
+from inspect import isawaitable
+from typing import cast, Any, Callable, Dict, List, Optional
+
+__all__ = ["EventEmitter", "EventEmitterAsyncIterator"]
+
+
+class EventEmitter:
+    """A very simple EventEmitter."""
+
+    def __init__(self, loop: Optional[AbstractEventLoop] = None) -> None:
+        self.loop = loop
+        self.listeners: Dict[str, List[Callable]] = defaultdict(list)
+
+    def add_listener(self, event_name: str, listener: Callable) -> "EventEmitter":
+        """Add a listener."""
+        self.listeners[event_name].append(listener)
+        return self
+
+    def remove_listener(self, event_name: str, listener: Callable) -> "EventEmitter":
+        """Removes a listener."""
+        self.listeners[event_name].remove(listener)
+        return self
+
+    def emit(self, event_name: str, *args: Any, **kwargs: Any) -> bool:
+        """Emit an event."""
+        listeners = list(self.listeners[event_name])
+        if not listeners:
+            return False
+        for listener in listeners:
+            result = listener(*args, **kwargs)
+            if isawaitable(result):
+                ensure_future(result, loop=self.loop)
+        return True
+
+
+class EventEmitterAsyncIterator:
+    """Create an AsyncIterator from an EventEmitter.
+
+    Useful for mocking a PubSub system for tests.
+    """
+
+    def __init__(self, event_emitter: EventEmitter, event_name: str) -> None:
+        self.queue: Queue = Queue(loop=cast(AbstractEventLoop, event_emitter.loop))
+        event_emitter.add_listener(event_name, self.queue.put)
+        self.remove_listener = lambda: event_emitter.remove_listener(
+            event_name, self.queue.put
+        )
+        self.closed = False
+
+    def __aiter__(self) -> "EventEmitterAsyncIterator":
+        return self
+
+    async def __anext__(self) -> Any:
+        if self.closed:
+            raise StopAsyncIteration
+        return await self.queue.get()
+
+    async def aclose(self) -> None:
+        self.remove_listener()
+        while not self.queue.empty():
+            await self.queue.get()
+        self.closed = True
diff --git a/src/graphql/pyutils/frozen_dict.py b/src/graphql/pyutils/frozen_dict.py
new file mode 100644
index 0000000..20999a8
--- /dev/null
+++ b/src/graphql/pyutils/frozen_dict.py
@@ -0,0 +1,48 @@
+from copy import deepcopy
+from typing import Dict, TypeVar
+
+from .frozen_error import FrozenError
+
+__all__ = ["FrozenDict"]
+
+K = TypeVar("K")
+T = TypeVar("T", covariant=True)
+
+
+class FrozenDict(Dict[K, T]):
+    """Dictionary that can only be read, but not changed."""
+
+    def __delitem__(self, key):
+        raise FrozenError
+
+    def __setitem__(self, key, value):
+        raise FrozenError
+
+    def __iadd__(self, value):
+        raise FrozenError
+
+    def __hash__(self):
+        return hash(tuple(self.items()))
+
+    def __copy__(self):
+        return FrozenDict(self)
+
+    copy = __copy__
+
+    def __deepcopy__(self, memo):
+        return FrozenDict({k: deepcopy(v, memo) for k, v in self.items()})
+
+    def clear(self):
+        raise FrozenError
+
+    def pop(self, key, default=None):
+        raise FrozenError
+
+    def popitem(self):
+        raise FrozenError
+
+    def setdefault(self, key, default=None):
+        raise FrozenError
+
+    def update(self, other=None):
+        raise FrozenError
diff --git a/src/graphql/pyutils/frozen_error.py b/src/graphql/pyutils/frozen_error.py
new file mode 100644
index 0000000..01c02d1
--- /dev/null
+++ b/src/graphql/pyutils/frozen_error.py
@@ -0,0 +1,5 @@
+__all__ = ["FrozenError"]
+
+
+class FrozenError(TypeError):
+    """Error when trying to change a frozen (read only) collection."""
diff --git a/src/graphql/pyutils/frozen_list.py b/src/graphql/pyutils/frozen_list.py
new file mode 100644
index 0000000..e1bd7eb
--- /dev/null
+++ b/src/graphql/pyutils/frozen_list.py
@@ -0,0 +1,66 @@
+from copy import deepcopy
+from typing import List, TypeVar
+
+from .frozen_error import FrozenError
+
+__all__ = ["FrozenList"]
+
+
+T = TypeVar("T", covariant=True)
+
+
+class FrozenList(List[T]):
+    """List that can only be read, but not changed."""
+
+    def __delitem__(self, key):
+        raise FrozenError
+
+    def __setitem__(self, key, value):
+        raise FrozenError
+
+    def __add__(self, value):
+        if isinstance(value, tuple):
+            value = list(value)
+        return list.__add__(self, value)
+
+    def __iadd__(self, value):
+        raise FrozenError
+
+    def __mul__(self, value):
+        return list.__mul__(self, value)
+
+    def __imul__(self, value):
+        raise FrozenError
+
+    def __hash__(self):
+        return hash(tuple(self))
+
+    def __copy__(self):
+        return FrozenList(self)
+
+    def __deepcopy__(self, memo):
+        return FrozenList(deepcopy(value, memo) for value in self)
+
+    def append(self, x):
+        raise FrozenError
+
+    def extend(self, iterable):
+        raise FrozenError
+
+    def insert(self, i, x):
+        raise FrozenError
+
+    def remove(self, x):
+        raise FrozenError
+
+    def pop(self, i=None):
+        raise FrozenError
+
+    def clear(self):
+        raise FrozenError
+
+    def sort(self, *, key=None, reverse=False):
+        raise FrozenError
+
+    def reverse(self):
+        raise FrozenError
diff --git a/src/graphql/pyutils/identity_func.py b/src/graphql/pyutils/identity_func.py
new file mode 100644
index 0000000..88a9673
--- /dev/null
+++ b/src/graphql/pyutils/identity_func.py
@@ -0,0 +1,13 @@
+from typing import cast, Any, TypeVar
+
+from .undefined import Undefined
+
+__all__ = ["identity_func"]
+
+
+T = TypeVar("T")
+
+
+def identity_func(x: T = cast(Any, Undefined), *_args: Any) -> T:
+    """Return the first received argument."""
+    return x
diff --git a/src/graphql/pyutils/inspect.py b/src/graphql/pyutils/inspect.py
new file mode 100644
index 0000000..8fc99dc
--- /dev/null
+++ b/src/graphql/pyutils/inspect.py
@@ -0,0 +1,180 @@
+from inspect import (
+    isclass,
+    ismethod,
+    isfunction,
+    isgeneratorfunction,
+    isgenerator,
+    iscoroutinefunction,
+    iscoroutine,
+    isasyncgenfunction,
+    isasyncgen,
+)
+from typing import Any, List
+
+from .undefined import Undefined
+
+__all__ = ["inspect"]
+
+max_recursive_depth = 2
+max_str_size = 240
+max_list_size = 10
+
+
+def inspect(value: Any) -> str:
+    """Inspect value and a return string representation for error messages.
+
+    Used to print values in error messages. We do not use repr() in order to not
+    leak too much of the inner Python representation of unknown objects, and we
+    do not use json.dumps() because not all objects can be serialized as JSON and
+    we want to output strings with single quotes like Python repr() does it.
+
+    We also restrict the size of the representation by truncating strings and
+    collections and allowing only a maximum recursion depth.
+    """
+    return inspect_recursive(value, [])
+
+
+def inspect_recursive(value: Any, seen_values: List) -> str:
+    if value is None or value is Undefined or isinstance(value, (bool, float, complex)):
+        return repr(value)
+    if isinstance(value, (int, str, bytes, bytearray)):
+        return trunc_str(repr(value))
+    if len(seen_values) < max_recursive_depth and value not in seen_values:
+        # check if we have a custom inspect method
+        inspect_method = getattr(value, "__inspect__", None)
+        if inspect_method is not None and callable(inspect_method):
+            s = inspect_method()
+            if isinstance(s, str):
+                return trunc_str(s)
+            seen_values = [*seen_values, value]
+            return inspect_recursive(s, seen_values)
+        # recursively inspect collections
+        if isinstance(value, (list, tuple, dict, set, frozenset)):
+            if not value:
+                return repr(value)
+            seen_values = [*seen_values, value]
+            if isinstance(value, list):
+                items = value
+            elif isinstance(value, dict):
+                items = list(value.items())
+            else:
+                items = list(value)
+            items = trunc_list(items)
+            if isinstance(value, dict):
+                s = ", ".join(
+                    "..."
+                    if v is ELLIPSIS
+                    else inspect_recursive(v[0], seen_values)
+                    + ": "
+                    + inspect_recursive(v[1], seen_values)
+                    for v in items
+                )
+            else:
+                s = ", ".join(
+                    "..." if v is ELLIPSIS else inspect_recursive(v, seen_values)
+                    for v in items
+                )
+            if isinstance(value, tuple):
+                if len(items) == 1:
+                    return f"({s},)"
+                return f"({s})"
+            if isinstance(value, (dict, set)):
+                return "{" + s + "}"
+            if isinstance(value, frozenset):
+                return f"frozenset({{{s}}})"
+            return f"[{s}]"
+    else:
+        # handle collections that are nested too deep
+        if isinstance(value, (list, tuple, dict, set, frozenset)):
+            if not value:
+                return repr(value)
+            if isinstance(value, list):
+                return "[...]"
+            if isinstance(value, tuple):
+                return "(...)"
+            if isinstance(value, dict):
+                return "{...}"
+            if isinstance(value, set):
+                return "set(...)"
+            return "frozenset(...)"
+    if isinstance(value, Exception):
+        type_ = "exception"
+        value = type(value)
+    elif isclass(value):
+        type_ = "exception class" if issubclass(value, Exception) else "class"
+    elif ismethod(value):
+        type_ = "method"
+    elif iscoroutinefunction(value):
+        type_ = "coroutine function"
+    elif isasyncgenfunction(value):
+        type_ = "async generator function"
+    elif isgeneratorfunction(value):
+        type_ = "generator function"
+    elif isfunction(value):
+        type_ = "function"
+    elif iscoroutine(value):
+        type_ = "coroutine"
+    elif isasyncgen(value):
+        type_ = "async generator"
+    elif isgenerator(value):
+        type_ = "generator"
+    else:
+        # stringify (only) the well-known GraphQL types
+        from ..type import (
+            GraphQLDirective,
+            GraphQLNamedType,
+            GraphQLScalarType,
+            GraphQLWrappingType,
+        )
+
+        if isinstance(
+            value,
+            (
+                GraphQLDirective,
+                GraphQLNamedType,
+                GraphQLScalarType,
+                GraphQLWrappingType,
+            ),
+        ):
+            return str(value)
+        try:
+            name = type(value).__name__
+            if not name or "<" in name or ">" in name:
+                raise AttributeError
+        except AttributeError:
+            return "<object>"
+        else:
+            return f"<{name} instance>"
+    try:
+        name = value.__name__
+        if not name or "<" in name or ">" in name:
+            raise AttributeError
+    except AttributeError:
+        return f"<{type_}>"
+    else:
+        return f"<{type_} {name}>"
+
+
+def trunc_str(s: str) -> str:
+    """Truncate strings to maximum length."""
+    if len(s) > max_str_size:
+        i = max(0, (max_str_size - 3) // 2)
+        j = max(0, max_str_size - 3 - i)
+        s = s[:i] + "..." + s[-j:]
+    return s
+
+
+def trunc_list(s: List) -> List:
+    """Truncate lists to maximum length."""
+    if len(s) > max_list_size:
+        i = max_list_size // 2
+        j = i - 1
+        s = s[:i] + [ELLIPSIS] + s[-j:]
+    return s
+
+
+class InspectEllipsisType:
+    """Singleton class for indicating ellipses in iterables."""
+
+
+ELLIPSIS = InspectEllipsisType()
diff --git a/src/graphql/pyutils/is_awaitable.py b/src/graphql/pyutils/is_awaitable.py
new file mode 100644
index 0000000..80c3be3
--- /dev/null
+++ b/src/graphql/pyutils/is_awaitable.py
@@ -0,0 +1,24 @@
+import inspect
+from typing import Any
+from types import CoroutineType, GeneratorType
+
+__all__ = ["is_awaitable"]
+
+CO_ITERABLE_COROUTINE = inspect.CO_ITERABLE_COROUTINE
+
+
+def is_awaitable(value: Any) -> bool:
+    """Return true if object can be passed to an ``await`` expression.
+
+    Instead of testing if the object is an instance of abc.Awaitable, it checks
+    the existence of an `__await__` attribute. This is much faster.
+    """
+    return (
+        # check for coroutine objects
+        isinstance(value, CoroutineType)
+        # check for old-style generator based coroutine objects
+        or isinstance(value, GeneratorType)
+        and bool(value.gi_code.co_flags & CO_ITERABLE_COROUTINE)
+        # check for other awaitables (e.g. futures)
+        or hasattr(value, "__await__")
+    )
diff --git a/src/graphql/pyutils/is_collection.py b/src/graphql/pyutils/is_collection.py
new file mode 100644
index 0000000..8c1ed92
--- /dev/null
+++ b/src/graphql/pyutils/is_collection.py
@@ -0,0 +1,15 @@
+from typing import Any, ByteString, Collection, Mapping, Text, ValuesView
+
+__all__ = ["is_collection"]
+
+collection_type: Any = Collection
+if not isinstance({}.values(), Collection):  # Python < 3.7.2
+    collection_type = (Collection, ValuesView)
+no_collection_type: Any = (ByteString, Mapping, Text)
+
+
+def is_collection(value: Any) -> bool:
+    """Check if value is a collection, but not a mapping and not a string."""
+    return isinstance(value, collection_type) and not isinstance(
+        value, no_collection_type
+    )
diff --git a/src/graphql/pyutils/is_finite.py b/src/graphql/pyutils/is_finite.py
new file mode 100644
index 0000000..8bd766a
--- /dev/null
+++ b/src/graphql/pyutils/is_finite.py
@@ -0,0 +1,11 @@
+from math import isfinite
+from typing import Any
+
+__all__ = ["is_finite"]
+
+
+def is_finite(value: Any) -> bool:
+    """Return true if a value is a finite number."""
+    return (isinstance(value, int) and not isinstance(value, bool)) or (
+        isinstance(value, float) and isfinite(value)
+    )
diff --git a/src/graphql/pyutils/is_integer.py b/src/graphql/pyutils/is_integer.py
new file mode 100644
index 0000000..9c6a0b3
--- /dev/null
+++ b/src/graphql/pyutils/is_integer.py
@@ -0,0 +1,11 @@
+from math import isfinite
+from typing import Any
+
+__all__ = ["is_integer"]
+
+
+def is_integer(value: Any) -> bool:
+    """Return true if a value is an integer number."""
+    return (isinstance(value, int) and not isinstance(value, bool)) or (
+        isinstance(value, float) and isfinite(value) and int(value) == value
+    )
diff --git a/src/graphql/pyutils/path.py b/src/graphql/pyutils/path.py
new file mode 100644
index 0000000..6603eaa
--- /dev/null
+++ b/src/graphql/pyutils/path.py
@@ -0,0 +1,26 @@
+from typing import Any, List, NamedTuple, Union
+
+__all__ = ["Path"]
+
+
+class Path(NamedTuple):
+    """A generic path of string or integer indices"""
+
+    prev: Any  # Optional['Path'] (python/mypy/issues/731)
+    """path with the previous indices"""
+    key: Union[str, int]
+    """current index in the path (string or integer)"""
+
+    def add_key(self, key: Union[str, int]) -> "Path":
+        """Return a new Path containing the given key."""
+        return Path(self, key)
+
+    def as_list(self) -> List[Union[str, int]]:
+        """Return a list of the path keys."""
+        flattened: List[Union[str, int]] = []
+        append = flattened.append
+        curr: Path = self
+        while curr:
+            append(curr.key)
+            curr = curr.prev
+        return flattened[::-1]
diff --git a/src/graphql/pyutils/print_path_list.py b/src/graphql/pyutils/print_path_list.py
new file mode 100644
index 0000000..125829b
--- /dev/null
+++ b/src/graphql/pyutils/print_path_list.py
@@ -0,0 +1,6 @@
+from typing import Collection, Union
+
+
+def print_path_list(path: Collection[Union[str, int]]) -> str:
+    """Build a string describing the path."""
+    return "".join(f"[{key}]" if isinstance(key, int) else f".{key}" for key in path)
diff --git a/src/graphql/pyutils/suggestion_list.py b/src/graphql/pyutils/suggestion_list.py
new file mode 100644
index 0000000..2936c0a
--- /dev/null
+++ b/src/graphql/pyutils/suggestion_list.py
@@ -0,0 +1,104 @@
+from typing import Collection, Optional, List
+
+__all__ = ["suggestion_list"]
+
+
+def suggestion_list(input_: str, options: Collection[str]) -> List[str]:
+    """Get list with suggestions for a given input.
+
+    Given an invalid input string and list of valid options, returns a filtered list
+    of valid options sorted based on their similarity with the input.
+    """
+    options_by_distance = {}
+    lexical_distance = LexicalDistance(input_)
+
+    threshold = int(len(input_) * 0.4) + 1
+    for option in options:
+        distance = lexical_distance.measure(option, threshold)
+        if distance is not None:
+            options_by_distance[option] = distance
+
+    # noinspection PyShadowingNames
+    return sorted(
+        options_by_distance,
+        key=lambda option: (options_by_distance.get(option, 0), option),
+    )
+
+
+class LexicalDistance:
+    """Computes the lexical distance between strings A and B.
+
+    The "distance" between two strings is given by counting the minimum number of edits
+    needed to transform string A into string B. An edit can be an insertion, deletion,
+    or substitution of a single character, or a swap of two adjacent characters.
+
+    This distance can be useful for detecting typos in input or sorting.
+    """
+
+    _input: str
+    _input_lower_case: str
+    _input_list: List[int]
+    _rows: List[List[int]]
+
+    def __init__(self, input_: str):
+        self._input = input_
+        self._input_lower_case = input_.lower()
+        row_size = len(input_) + 1
+        self._input_list = list(map(ord, self._input_lower_case))
+
+        self._rows = [[0] * row_size, [0] * row_size, [0] * row_size]
+
+    def measure(self, option: str, threshold: int) -> Optional[int]:
+        if self._input == option:
+            return 0
+
+        option_lower_case = option.lower()
+
+        # Any case change counts as a single edit
+        if self._input_lower_case == option_lower_case:
+            return 1
+
+        a, b = list(map(ord, option_lower_case)), self._input_list
+        a_len, b_len = len(a), len(b)
+        if a_len < b_len:
+            a, b = b, a
+            a_len, b_len = b_len, a_len
+
+        if a_len - b_len > threshold:
+            return None
+
+        rows = self._rows
+        for j in range(0, b_len + 1):
+            rows[0][j] = j
+
+        for i in range(1, a_len + 1):
+            up_row = rows[(i - 1) % 3]
+            current_row = rows[i % 3]
+
+            smallest_cell = current_row[0] = i
+            for j in range(1, b_len + 1):
+                cost = 0 if a[i - 1] == b[j - 1] else 1
+
+                current_cell = min(
+                    up_row[j] + 1,  # delete
+                    current_row[j - 1] + 1,  # insert
+                    up_row[j - 1] + cost,  # substitute
+                )
+
+                if i > 1 and j > 1 and a[i - 1] == b[j - 2] and a[i - 2] == b[j - 1]:
+                    # transposition
+                    double_diagonal_cell = rows[(i - 2) % 3][j - 2]
+                    current_cell = min(current_cell, double_diagonal_cell + 1)
+
+                if current_cell < smallest_cell:
+                    smallest_cell = current_cell
+
+                current_row[j] = current_cell
+
+            # Early exit, since distance can't go smaller than smallest element
+            # of the previous row.
+            if smallest_cell > threshold:
+                return None
+
+        distance = rows[a_len % 3][b_len]
+        return distance if distance <= threshold else None
diff --git a/src/graphql/pyutils/undefined.py b/src/graphql/pyutils/undefined.py
new file mode 100644
index 0000000..73dc531
--- /dev/null
+++ b/src/graphql/pyutils/undefined.py
@@ -0,0 +1,34 @@
+from typing import Any
+
+__all__ = ["Undefined", "UndefinedType"]
+
+
+class UndefinedType(ValueError):
+    """Auxiliary class for creating the Undefined singleton."""
+
+    def __repr__(self) -> str:
+        return "Undefined"
+
+    __str__ = __repr__
+
+    def __hash__(self) -> int:
+        return hash(UndefinedType)
+
+    def __bool__(self) -> bool:
+        return False
+
+    def __eq__(self, other: Any) -> bool:
+        return other is Undefined
+
+    def __ne__(self, other: Any) -> bool:
+        return not self == other
+
+
+# Used to indicate undefined or invalid values (like "undefined" in JavaScript):
+Undefined = UndefinedType()
+
+Undefined.__doc__ = """Symbol for undefined values
+
+This singleton object is used to describe undefined or invalid  values.
+It can be used in places where you would use ``undefined`` in GraphQL.js.
+"""
diff --git a/src/graphql/subscription/__init__.py b/src/graphql/subscription/__init__.py
new file mode 100644
index 0000000..9f8c45d
--- /dev/null
+++ b/src/graphql/subscription/__init__.py
@@ -0,0 +1,9 @@
+"""GraphQL Subscription
+
+The :mod:`graphql.subscription` package is responsible for subscribing to updates
+on specific data.
+"""
+
+from .subscribe import subscribe, create_source_event_stream
+
+__all__ = ["subscribe", "create_source_event_stream"]
diff --git a/src/graphql/subscription/map_async_iterator.py b/src/graphql/subscription/map_async_iterator.py
new file mode 100644
index 0000000..35fd42a
--- /dev/null
+++ b/src/graphql/subscription/map_async_iterator.py
@@ -0,0 +1,111 @@
+from asyncio import Event, ensure_future, Future, wait
+from concurrent.futures import FIRST_COMPLETED
+from inspect import isasyncgen, isawaitable
+from typing import cast, Any, AsyncIterable, Callable, Optional, Set, Type, Union
+from types import TracebackType
+
+__all__ = ["MapAsyncIterator"]
+
+
+# noinspection PyAttributeOutsideInit
+class MapAsyncIterator:
+    """Map an AsyncIterable over a callback function.
+
+    Given an AsyncIterable and a callback function, return an AsyncIterator which
+    produces values mapped via calling the callback function.
+
+    When the resulting AsyncIterator is closed, the underlying AsyncIterable will also
+    be closed.
+    """
+
+    def __init__(
+        self,
+        iterable: AsyncIterable,
+        callback: Callable,
+        reject_callback: Optional[Callable] = None,
+    ) -> None:
+        self.iterator = iterable.__aiter__()
+        self.callback = callback
+        self.reject_callback = reject_callback
+        self._close_event = Event()
+
+    def __aiter__(self) -> "MapAsyncIterator":
+        return self
+
+    async def __anext__(self) -> Any:
+        if self.is_closed:
+            if not isasyncgen(self.iterator):
+                raise StopAsyncIteration
+            value = await self.iterator.__anext__()
+            result = self.callback(value)
+
+        else:
+            aclose = ensure_future(self._close_event.wait())
+            anext = ensure_future(self.iterator.__anext__())
+
+            pending: Set[Future] = (
+                await wait([aclose, anext], return_when=FIRST_COMPLETED)
+            )[1]
+            for task in pending:
+                task.cancel()
+
+            if aclose.done():
+                raise StopAsyncIteration
+
+            error = anext.exception()
+            if error:
+                if not self.reject_callback or isinstance(
+                    error, (StopAsyncIteration, GeneratorExit)
+                ):
+                    raise error
+                result = self.reject_callback(error)
+            else:
+                value = anext.result()
+                result = self.callback(value)
+
+        return await result if isawaitable(result) else result
+
+    async def athrow(
+        self,
+        type_: Union[BaseException, Type[BaseException]],
+        value: Optional[BaseException] = None,
+        traceback: Optional[TracebackType] = None,
+    ) -> None:
+        if not self.is_closed:
+            athrow = getattr(self.iterator, "athrow", None)
+            if athrow:
+                await athrow(type_, value, traceback)
+            else:
+                await self.aclose()
+                if value is None:
+                    if traceback is None:
+                        raise type_
+                    value = (
+                        type_
+                        if isinstance(value, BaseException)
+                        else cast(Type[BaseException], type_)()
+                    )
+                if traceback is not None:
+                    value = value.with_traceback(traceback)
+                raise value
+
+    async def aclose(self) -> None:
+        if not self.is_closed:
+            aclose = getattr(self.iterator, "aclose", None)
+            if aclose:
+                try:
+                    await aclose()
+                except RuntimeError:
+                    pass
+            self.is_closed = True
+
+    @property
+    def is_closed(self) -> bool:
+        return self._close_event.is_set()
+
+    @is_closed.setter
+    def is_closed(self, value: bool) -> None:
+        if value:
+            self._close_event.set()
+        else:
+            self._close_event.clear()
diff --git a/src/graphql/subscription/subscribe.py b/src/graphql/subscription/subscribe.py
new file mode 100644
index 0000000..b545f8f
--- /dev/null
+++ b/src/graphql/subscription/subscribe.py
@@ -0,0 +1,185 @@
+from inspect import isawaitable
+from typing import (
+    Any,
+    AsyncIterable,
+    AsyncIterator,
+    Awaitable,
+    Dict,
+    Optional,
+    Union,
+    cast,
+)
+
+from ..error import GraphQLError, located_error
+from ..execution.execute import (
+    assert_valid_execution_arguments,
+    execute,
+    get_field_def,
+    ExecutionContext,
+    ExecutionResult,
+)
+from ..language import DocumentNode
+from ..pyutils import Path, inspect
+from ..type import GraphQLFieldResolver, GraphQLSchema
+from ..utilities import get_operation_root_type
+from .map_async_iterator import MapAsyncIterator
+
+__all__ = ["subscribe", "create_source_event_stream"]
+
+
+async def subscribe(
+    schema: GraphQLSchema,
+    document: DocumentNode,
+    root_value: Any = None,
+    context_value: Any = None,
+    variable_values: Optional[Dict[str, Any]] = None,
+    operation_name: Optional[str] = None,
+    field_resolver: Optional[GraphQLFieldResolver] = None,
+    subscribe_field_resolver: Optional[GraphQLFieldResolver] = None,
+) -> Union[AsyncIterator[ExecutionResult], ExecutionResult]:
+    """Create a GraphQL subscription.
+
+    Implements the "Subscribe" algorithm described in the GraphQL spec.
+
+    Returns a coroutine object which yields either an AsyncIterator (if successful) or
+    an ExecutionResult (client error). The coroutine will raise an exception if a server
+    error occurs.
+
+    If the client-provided arguments to this function do not result in a compliant
+    subscription, a GraphQL Response (ExecutionResult) with descriptive errors and no
+    data will be returned.
+
+    If the source stream could not be created due to faulty subscription resolver logic
+    or underlying systems, the coroutine object will yield a single ExecutionResult
+    containing ``errors`` and no ``data``.
+
+    If the operation succeeded, the coroutine will yield an AsyncIterator, which yields
+    a stream of ExecutionResults representing the response stream.
+    """
+    try:
+        result_or_stream = await create_source_event_stream(
+            schema,
+            document,
+            root_value,
+            context_value,
+            variable_values,
+            operation_name,
+            subscribe_field_resolver,
+        )
+    except GraphQLError as error:
+        return ExecutionResult(data=None, errors=[error])
+    if isinstance(result_or_stream, ExecutionResult):
+        return result_or_stream
+
+    async def map_source_to_response(payload: Any) -> ExecutionResult:
+        """Map source to response.
+
+        For each payload yielded from a subscription, map it over the normal GraphQL
+        :func:`~graphql.execute` function, with ``payload`` as the ``root_value``.
+        This implements the "MapSourceToResponseEvent" algorithm described in the
+        GraphQL specification. The :func:`~graphql.execute` function provides the
+        "ExecuteSubscriptionEvent" algorithm, as it is nearly identical to the
+        "ExecuteQuery" algorithm, for which :func:`~graphql.execute` is also used.
+        """
+        result = execute(
+            schema,
+            document,
+            payload,
+            context_value,
+            variable_values,
+            operation_name,
+            field_resolver,
+        )
+        return await result if isawaitable(result) else result  # type: ignore
+
+    return MapAsyncIterator(result_or_stream, map_source_to_response)
+
+
+async def create_source_event_stream(
+    schema: GraphQLSchema,
+    document: DocumentNode,
+    root_value: Any = None,
+    context_value: Any = None,
+    variable_values: Optional[Dict[str, Any]] = None,
+    operation_name: Optional[str] = None,
+    field_resolver: Optional[GraphQLFieldResolver] = None,
+) -> Union[AsyncIterable[Any], ExecutionResult]:
+    """Create source even stream
+
+    Implements the "CreateSourceEventStream" algorithm described in the GraphQL
+    specification, resolving the subscription source event stream.
+
+    Returns a coroutine that yields an AsyncIterable.
+
+    If the client provided invalid arguments, the source stream could not be created,
+    or the resolver did not return an AsyncIterable, this function will throw an error,
+    which should be caught and handled by the caller.
+
+    A Source Event Stream represents a sequence of events, each of which triggers a
+    GraphQL execution for that event.
+
+    This may be useful when hosting the stateful subscription service in a different
+    process or machine than the stateless GraphQL execution engine, or otherwise
+    separating these two steps. For more on this, see the "Supporting Subscriptions
+    at Scale" information in the GraphQL spec.
+    """
+    # If arguments are missing or incorrectly typed, this is an internal developer
+    # mistake which should throw an early error.
+    assert_valid_execution_arguments(schema, document, variable_values)
+
+    # If a valid context cannot be created due to incorrect arguments, this will throw
+    # an error.
+    context = ExecutionContext.build(
+        schema,
+        document,
+        root_value,
+        context_value,
+        variable_values,
+        operation_name,
+        field_resolver,
+    )
+
+    # Return early errors if execution context failed.
+    if isinstance(context, list):
+        return ExecutionResult(data=None, errors=context)
+
+    type_ = get_operation_root_type(schema, context.operation)
+    fields = context.collect_fields(type_, context.operation.selection_set, {}, set())
+    response_names = list(fields)
+    response_name = response_names[0]
+    field_nodes = fields[response_name]
+    field_node = field_nodes[0]
+    field_name = field_node.name.value
+    field_def = get_field_def(schema, type_, field_name)
+
+    if not field_def:
+        raise GraphQLError(
+            f"The subscription field '{field_name}' is not defined.", field_nodes
+        )
+
+    # Call the `subscribe()` resolver or the default resolver to produce an
+    # AsyncIterable yielding raw payloads.
+    resolve_fn = field_def.subscribe or context.field_resolver
+
+    path = Path(None, response_name)
+
+    info = context.build_resolve_info(field_def, field_nodes, type_, path)
+
+    # `resolve_field_value_or_error` implements the "ResolveFieldEventStream" algorithm
+    # from GraphQL specification. It differs from `resolve_field_value` due to
+    # providing a different `resolve_fn`.
+    result = context.resolve_field_value_or_error(
+        field_def, field_nodes, resolve_fn, root_value, info
+    )
+    event_stream = await cast(Awaitable, result) if isawaitable(result) else result
+    # If `event_stream` is an Error, rethrow a located error.
+    if isinstance(event_stream, Exception):
+        raise located_error(event_stream, field_nodes, path.as_list())
+
+    # Assert field returned an event stream, otherwise yield an error.
+    if isinstance(event_stream, AsyncIterable):
+        return event_stream
+    raise TypeError(
+        "Subscription field must return AsyncIterable."
+        f" Received: {inspect(event_stream)}."
+    )
diff --git a/src/graphql/type/__init__.py b/src/graphql/type/__init__.py
new file mode 100644
index 0000000..fbdb150
--- /dev/null
+++ b/src/graphql/type/__init__.py
@@ -0,0 +1,248 @@
+"""GraphQL Type System
+
+The :mod:`graphql.type` package is responsible for defining GraphQL types and schema.
+"""
+
+from ..pyutils import Path as ResponsePath
+
+from .schema import (
+    # Predicate
+    is_schema,
+    # Assertion
+    assert_schema,
+    # GraphQL Schema definition
+    GraphQLSchema,
+)
+
+from .definition import (
+    # Predicates
+    is_type,
+    is_scalar_type,
+    is_object_type,
+    is_interface_type,
+    is_union_type,
+    is_enum_type,
+    is_input_object_type,
+    is_list_type,
+    is_non_null_type,
+    is_input_type,
+    is_output_type,
+    is_leaf_type,
+    is_composite_type,
+    is_abstract_type,
+    is_wrapping_type,
+    is_nullable_type,
+    is_named_type,
+    is_required_argument,
+    is_required_input_field,
+    # Assertions
+    assert_type,
+    assert_scalar_type,
+    assert_object_type,
+    assert_interface_type,
+    assert_union_type,
+    assert_enum_type,
+    assert_input_object_type,
+    assert_list_type,
+    assert_non_null_type,
+    assert_input_type,
+    assert_output_type,
+    assert_leaf_type,
+    assert_composite_type,
+    assert_abstract_type,
+    assert_wrapping_type,
+    assert_nullable_type,
+    assert_named_type,
+    # Un-modifiers
+    get_nullable_type,
+    get_named_type,
+    # Definitions
+    GraphQLScalarType,
+    GraphQLObjectType,
+    GraphQLInterfaceType,
+    GraphQLUnionType,
+    GraphQLEnumType,
+    GraphQLInputObjectType,
+    # Type Wrappers
+    GraphQLList,
+    GraphQLNonNull,
+    # Types
+    GraphQLType,
+    GraphQLInputType,
+    GraphQLOutputType,
+    GraphQLLeafType,
+    GraphQLCompositeType,
+    GraphQLAbstractType,
+    GraphQLWrappingType,
+    GraphQLNullableType,
+    GraphQLNamedType,
+    Thunk,
+    GraphQLArgument,
+    GraphQLArgumentMap,
+    GraphQLEnumValue,
+    GraphQLEnumValueMap,
+    GraphQLField,
+    GraphQLFieldMap,
+    GraphQLInputField,
+    GraphQLInputFieldMap,
+    GraphQLScalarSerializer,
+    GraphQLScalarValueParser,
+    GraphQLScalarLiteralParser,
+    # Resolvers
+    GraphQLFieldResolver,
+    GraphQLTypeResolver,
+    GraphQLIsTypeOfFn,
+    GraphQLResolveInfo,
+)
+
+from .directives import (
+    # Predicate
+    is_directive,
+    # Assertion
+    assert_directive,
+    # Directives Definition
+    GraphQLDirective,
+    # Built-in Directives defined by the Spec
+    is_specified_directive,
+    specified_directives,
+    GraphQLIncludeDirective,
+    GraphQLSkipDirective,
+    GraphQLDeprecatedDirective,
+    GraphQLSpecifiedByDirective,
+    # Constant Deprecation Reason
+    DEFAULT_DEPRECATION_REASON,
+)
+
+# Common built-in scalar instances.
+from .scalars import (
+    # Predicate
+    is_specified_scalar_type,
+    # Standard GraphQL Scalars
+    specified_scalar_types,
+    GraphQLInt,
+    GraphQLFloat,
+    GraphQLString,
+    GraphQLBoolean,
+    GraphQLID,
+)
+
+from .introspection import (
+    # Predicate
+    is_introspection_type,
+    # GraphQL Types for introspection.
+    introspection_types,
+    # "Enum" of Type Kinds
+    TypeKind,
+    # Meta-field definitions.
+    SchemaMetaFieldDef,
+    TypeMetaFieldDef,
+    TypeNameMetaFieldDef,
+)
+
+# Validate GraphQL schema.
+from .validate import validate_schema, assert_valid_schema
+
+__all__ = [
+    "is_schema",
+    "assert_schema",
+    "GraphQLSchema",
+    "is_type",
+    "is_scalar_type",
+    "is_object_type",
+    "is_interface_type",
+    "is_union_type",
+    "is_enum_type",
+    "is_input_object_type",
+    "is_list_type",
+    "is_non_null_type",
+    "is_input_type",
+    "is_output_type",
+    "is_leaf_type",
+    "is_composite_type",
+    "is_abstract_type",
+    "is_wrapping_type",
+    "is_nullable_type",
+    "is_named_type",
+    "is_required_argument",
+    "is_required_input_field",
+    "assert_type",
+    "assert_scalar_type",
+    "assert_object_type",
+    "assert_interface_type",
+    "assert_union_type",
+    "assert_enum_type",
+    "assert_input_object_type",
+    "assert_list_type",
+    "assert_non_null_type",
+    "assert_input_type",
+    "assert_output_type",
+    "assert_leaf_type",
+    "assert_composite_type",
+    "assert_abstract_type",
+    "assert_wrapping_type",
+    "assert_nullable_type",
+    "assert_named_type",
+    "get_nullable_type",
+    "get_named_type",
+    "GraphQLScalarType",
+    "GraphQLObjectType",
+    "GraphQLInterfaceType",
+    "GraphQLUnionType",
+    "GraphQLEnumType",
+    "GraphQLInputObjectType",
+    "GraphQLInputType",
+    "GraphQLArgument",
+    "GraphQLList",
+    "GraphQLNonNull",
+    "GraphQLType",
+    "GraphQLInputType",
+    "GraphQLOutputType",
+    "GraphQLLeafType",
+    "GraphQLCompositeType",
+    "GraphQLAbstractType",
+    "GraphQLWrappingType",
+    "GraphQLNullableType",
+    "GraphQLNamedType",
+    "Thunk",
+    "GraphQLArgument",
+    "GraphQLArgumentMap",
+    "GraphQLEnumValue",
+    "GraphQLEnumValueMap",
+    "GraphQLField",
+    "GraphQLFieldMap",
+    "GraphQLInputField",
+    "GraphQLInputFieldMap",
+    "GraphQLScalarSerializer",
+    "GraphQLScalarValueParser",
+    "GraphQLScalarLiteralParser",
+    "GraphQLFieldResolver",
+    "GraphQLTypeResolver",
+    "GraphQLIsTypeOfFn",
+    "GraphQLResolveInfo",
+    "ResponsePath",
+    "is_directive",
+    "assert_directive",
+    "is_specified_directive",
+    "specified_directives",
+    "GraphQLDirective",
+    "GraphQLIncludeDirective",
+    "GraphQLSkipDirective",
+    "GraphQLDeprecatedDirective",
+    "GraphQLSpecifiedByDirective",
+    "DEFAULT_DEPRECATION_REASON",
+    "is_specified_scalar_type",
+    "specified_scalar_types",
+    "GraphQLInt",
+    "GraphQLFloat",
+    "GraphQLString",
+    "GraphQLBoolean",
+    "GraphQLID",
+    "is_introspection_type",
+    "introspection_types",
+    "TypeKind",
+    "SchemaMetaFieldDef",
+    "TypeMetaFieldDef",
+    "TypeNameMetaFieldDef",
+    "validate_schema",
+    "assert_valid_schema",
+]
diff --git a/src/graphql/type/definition.py b/src/graphql/type/definition.py
new file mode 100644
index 0000000..c2e54d0
--- /dev/null
+++ b/src/graphql/type/definition.py
@@ -0,0 +1,1679 @@
+from enum import Enum
+from typing import (
+    Any,
+    Callable,
+    Collection,
+    Dict,
+    Generic,
+    List,
+    NamedTuple,
+    Optional,
+    TYPE_CHECKING,
+    Type,
+    TypeVar,
+    Union,
+    cast,
+    overload,
+)
+
+from ..error import GraphQLError
+from ..language import (
+    EnumTypeDefinitionNode,
+    EnumValueDefinitionNode,
+    EnumTypeExtensionNode,
+    EnumValueNode,
+    FieldDefinitionNode,
+    FieldNode,
+    FragmentDefinitionNode,
+    InputObjectTypeDefinitionNode,
+    InputObjectTypeExtensionNode,
+    InputValueDefinitionNode,
+    InterfaceTypeDefinitionNode,
+    InterfaceTypeExtensionNode,
+    ObjectTypeDefinitionNode,
+    ObjectTypeExtensionNode,
+    OperationDefinitionNode,
+    ScalarTypeDefinitionNode,
+    ScalarTypeExtensionNode,
+    TypeDefinitionNode,
+    TypeExtensionNode,
+    UnionTypeDefinitionNode,
+    UnionTypeExtensionNode,
+    ValueNode,
+    print_ast,
+)
+from ..pyutils import (
+    AwaitableOrValue,
+    FrozenList,
+    Path,
+    cached_property,
+    did_you_mean,
+    inspect,
+    is_collection,
+    is_description,
+    suggestion_list,
+    Undefined,
+)
+from ..utilities.value_from_ast_untyped import value_from_ast_untyped
+
+if TYPE_CHECKING:
+    from .schema import GraphQLSchema  # noqa: F401
+
+__all__ = [
+    "is_type",
+    "is_scalar_type",
+    "is_object_type",
+    "is_interface_type",
+    "is_union_type",
+    "is_enum_type",
+    "is_input_object_type",
+    "is_list_type",
+    "is_non_null_type",
+    "is_input_type",
+    "is_output_type",
+    "is_leaf_type",
+    "is_composite_type",
+    "is_abstract_type",
+    "is_wrapping_type",
+    "is_nullable_type",
+    "is_named_type",
+    "is_required_argument",
+    "is_required_input_field",
+    "assert_type",
+    "assert_scalar_type",
+    "assert_object_type",
+    "assert_interface_type",
+    "assert_union_type",
+    "assert_enum_type",
+    "assert_input_object_type",
+    "assert_list_type",
+    "assert_non_null_type",
+    "assert_input_type",
+    "assert_output_type",
+    "assert_leaf_type",
+    "assert_composite_type",
+    "assert_abstract_type",
+    "assert_wrapping_type",
+    "assert_nullable_type",
+    "assert_named_type",
+    "get_nullable_type",
+    "get_named_type",
+    "GraphQLAbstractType",
+    "GraphQLArgument",
+    "GraphQLArgumentMap",
+    "GraphQLCompositeType",
+    "GraphQLEnumType",
+    "GraphQLEnumValue",
+    "GraphQLEnumValueMap",
+    "GraphQLField",
+    "GraphQLFieldMap",
+    "GraphQLFieldResolver",
+    "GraphQLInputField",
+    "GraphQLInputFieldMap",
+    "GraphQLInputObjectType",
+    "GraphQLInputType",
+    "GraphQLInterfaceType",
+    "GraphQLIsTypeOfFn",
+    "GraphQLLeafType",
+    "GraphQLList",
+    "GraphQLNamedType",
+    "GraphQLNullableType",
+    "GraphQLNonNull",
+    "GraphQLResolveInfo",
+    "GraphQLScalarType",
+    "GraphQLScalarSerializer",
+    "GraphQLScalarValueParser",
+    "GraphQLScalarLiteralParser",
+    "GraphQLObjectType",
+    "GraphQLOutputType",
+    "GraphQLType",
+    "GraphQLTypeResolver",
+    "GraphQLUnionType",
+    "GraphQLWrappingType",
+    "Thunk",
+]
+
+
+class GraphQLType:
+    """Base class for all GraphQL types"""
+
+    # Note: We don't use slots for GraphQLType objects because memory considerations
+    # are not really important for the schema definition and it would make caching
+    # properties slower or more complicated.
+
+
+# There are predicates for each kind of GraphQL type.
+
+
+def is_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLType)
+
+
+def assert_type(type_: Any) -> GraphQLType:
+    if not is_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL type.")
+    return cast(GraphQLType, type_)
+
+
+# These types wrap and modify other types
+
+GT = TypeVar("GT", bound=GraphQLType)
+
+
+class GraphQLWrappingType(GraphQLType, Generic[GT]):
+    """Base class for all GraphQL wrapping types"""
+
+    of_type: GT
+
+    def __init__(self, type_: GT) -> None:
+        if not is_type(type_):
+            raise TypeError(
+                f"Can only create a wrapper for a GraphQLType, but got: {type_}."
+            )
+        self.of_type = type_
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__} {self.of_type!r}>"
+
+
+def is_wrapping_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLWrappingType)
+
+
+def assert_wrapping_type(type_: Any) -> GraphQLWrappingType:
+    if not is_wrapping_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL wrapping type.")
+    return cast(GraphQLWrappingType, type_)
+
+
+# These named types do not include modifiers like List or NonNull.
+
+
+class GraphQLNamedType(GraphQLType):
+    """Base class for all GraphQL named types"""
+
+    name: str
+    description: Optional[str]
+    extensions: Optional[Dict[str, Any]]
+    ast_node: Optional[TypeDefinitionNode]
+    extension_ast_nodes: Optional[FrozenList[TypeExtensionNode]]
+
+    def __init__(
+        self,
+        name: str,
+        description: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[TypeDefinitionNode] = None,
+        extension_ast_nodes: Optional[Collection[TypeExtensionNode]] = None,
+    ) -> None:
+        if not name:
+            raise TypeError("Must provide name.")
+        if not isinstance(name, str):
+            raise TypeError("The name must be a string.")
+        if description is not None and not is_description(description):
+            raise TypeError("The description must be a string.")
+        if extensions is not None and (
+            not isinstance(extensions, dict)
+            or not all(isinstance(key, str) for key in extensions)
+        ):
+            raise TypeError(f"{name} extensions must be a dictionary with string keys.")
+        if ast_node and not isinstance(ast_node, TypeDefinitionNode):
+            raise TypeError(f"{name} AST node must be a TypeDefinitionNode.")
+        if extension_ast_nodes:
+            if not is_collection(extension_ast_nodes) or not all(
+                isinstance(node, TypeExtensionNode) for node in extension_ast_nodes
+            ):
+                raise TypeError(
+                    f"{name} extension AST nodes must be specified"
+                    " as a collection of TypeExtensionNode instances."
+                )
+            if not isinstance(extension_ast_nodes, FrozenList):
+                extension_ast_nodes = FrozenList(extension_ast_nodes)
+        else:
+            extension_ast_nodes = None
+        self.name = name
+        self.description = description
+        self.extensions = extensions
+        self.ast_node = ast_node
+        self.extension_ast_nodes = extension_ast_nodes
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__} {self.name!r}>"
+
+    def __str__(self) -> str:
+        return self.name
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            name=self.name,
+            description=self.description,
+            extensions=self.extensions,
+            ast_node=self.ast_node,
+            extension_ast_nodes=self.extension_ast_nodes or FrozenList(),
+        )
+
+
+def is_named_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLNamedType)
+
+
+def assert_named_type(type_: Any) -> GraphQLNamedType:
+    if not is_named_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL named type.")
+    return cast(GraphQLNamedType, type_)
+
+
+@overload
+def get_named_type(type_: None) -> None:
+    ...
+
+
+@overload
+def get_named_type(type_: GraphQLType) -> GraphQLNamedType:
+    ...
+
+
+def get_named_type(type_: Optional[GraphQLType]) -> Optional[GraphQLNamedType]:
+    """Unwrap possible wrapping type"""
+    if type_:
+        unwrapped_type = type_
+        while is_wrapping_type(unwrapped_type):
+            unwrapped_type = cast(GraphQLWrappingType, unwrapped_type)
+            unwrapped_type = unwrapped_type.of_type
+        return cast(GraphQLNamedType, unwrapped_type)
+    return None
+
+
+def resolve_thunk(thunk: Any) -> Any:
+    """Resolve the given thunk.
+
+    Used while defining GraphQL types to allow for circular references in otherwise
+    immutable type definitions.
+    """
+    return thunk() if callable(thunk) else thunk
+
+
+GraphQLScalarSerializer = Callable[[Any], Any]
+GraphQLScalarValueParser = Callable[[Any], Any]
+GraphQLScalarLiteralParser = Callable[[ValueNode, Optional[Dict[str, Any]]], Any]
+
+
+class GraphQLScalarType(GraphQLNamedType):
+    """Scalar Type Definition
+
+    The leaf values of any request and input values to arguments are Scalars (or Enums)
+    and are defined with a name and a series of functions used to parse input from ast
+    or variables and to ensure validity.
+
+    If a type's serialize function does not return a value (i.e. it returns ``None``),
+    then no error will be included in the response.
+
+    Example::
+
+        def serialize_odd(value):
+            if value % 2 == 1:
+                return value
+
+        odd_type = GraphQLScalarType('Odd', serialize=serialize_odd)
+
+    """
+
+    specified_by_url: Optional[str]
+    ast_node: Optional[ScalarTypeDefinitionNode]
+    extension_ast_nodes: Optional[FrozenList[ScalarTypeExtensionNode]]
+
+    def __init__(
+        self,
+        name: str,
+        serialize: Optional[GraphQLScalarSerializer] = None,
+        parse_value: Optional[GraphQLScalarValueParser] = None,
+        parse_literal: Optional[GraphQLScalarLiteralParser] = None,
+        description: Optional[str] = None,
+        specified_by_url: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[ScalarTypeDefinitionNode] = None,
+        extension_ast_nodes: Optional[Collection[ScalarTypeExtensionNode]] = None,
+    ) -> None:
+        super().__init__(
+            name=name,
+            description=description,
+            extensions=extensions,
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        if specified_by_url is not None and not isinstance(specified_by_url, str):
+            raise TypeError(
+                f"{name} must provide 'specified_by_url' as a string,"
+                f" but got: {inspect(specified_by_url)}."
+            )
+        if serialize is not None and not callable(serialize):
+            raise TypeError(
+                f"{name} must provide 'serialize' as a function."
+                " If this custom Scalar is also used as an input type,"
+                " ensure 'parse_value' and 'parse_literal' functions"
+                " are also provided."
+            )
+        if parse_literal is not None and (
+            not callable(parse_literal)
+            or (parse_value is None or not callable(parse_value))
+        ):
+            raise TypeError(
+                f"{name} must provide"
+                " both 'parse_value' and 'parse_literal' as functions."
+            )
+        if ast_node and not isinstance(ast_node, ScalarTypeDefinitionNode):
+            raise TypeError(f"{name} AST node must be a ScalarTypeDefinitionNode.")
+        if extension_ast_nodes and not all(
+            isinstance(node, ScalarTypeExtensionNode) for node in extension_ast_nodes
+        ):
+            raise TypeError(
+                f"{name} extension AST nodes must be specified"
+                " as a collection of ScalarTypeExtensionNode instances."
+            )
+        if serialize is not None:
+            self.serialize = serialize  # type: ignore
+        if parse_value is not None:
+            self.parse_value = parse_value  # type: ignore
+        if parse_literal is not None:
+            self.parse_literal = parse_literal  # type: ignore
+        self.specified_by_url = specified_by_url
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__} {self.name!r}>"
+
+    def __str__(self) -> str:
+        return self.name
+
+    @staticmethod
+    def serialize(value: Any) -> Any:
+        """Serializes an internal value to include in a response.
+
+        This default method just passes the value through and should be replaced
+        with a more specific version when creating a scalar type.
+        """
+        return value
+
+    @staticmethod
+    def parse_value(value: Any) -> Any:
+        """Parses an externally provided value to use as an input.
+
+        This default method just passes the value through and should be replaced
+        with a more specific version when creating a scalar type.
+        """
+        return value
+
+    def parse_literal(
+        self, node: ValueNode, _variables: Optional[Dict[str, Any]] = None
+    ) -> Any:
+        """Parses an externally provided literal value to use as an input.
+
+        This default method uses the parse_value method and should be replaced
+        with a more specific version when creating a scalar type.
+        """
+        return self.parse_value(value_from_ast_untyped(node))
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            **super().to_kwargs(),
+            serialize=None
+            if self.serialize is GraphQLScalarType.serialize
+            else self.serialize,
+            parse_value=None
+            if self.parse_value is GraphQLScalarType.parse_value
+            else self.parse_value,
+            parse_literal=None
+            if getattr(self.parse_literal, "__func__", None)
+            is GraphQLScalarType.parse_literal
+            else self.parse_literal,
+            specified_by_url=self.specified_by_url,
+        )
+
+
+def is_scalar_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLScalarType)
+
+
+def assert_scalar_type(type_: Any) -> GraphQLScalarType:
+    if not is_scalar_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL Scalar type.")
+    return cast(GraphQLScalarType, type_)
+
+
+GraphQLArgumentMap = Dict[str, "GraphQLArgument"]
+
+
+class GraphQLField:
+    """Definition of a GraphQL field"""
+
+    type: "GraphQLOutputType"
+    args: GraphQLArgumentMap
+    resolve: Optional["GraphQLFieldResolver"]
+    subscribe: Optional["GraphQLFieldResolver"]
+    description: Optional[str]
+    deprecation_reason: Optional[str]
+    extensions: Optional[Dict[str, Any]]
+    ast_node: Optional[FieldDefinitionNode]
+
+    def __init__(
+        self,
+        type_: "GraphQLOutputType",
+        args: Optional[GraphQLArgumentMap] = None,
+        resolve: Optional["GraphQLFieldResolver"] = None,
+        subscribe: Optional["GraphQLFieldResolver"] = None,
+        description: Optional[str] = None,
+        deprecation_reason: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[FieldDefinitionNode] = None,
+    ) -> None:
+        if not is_output_type(type_):
+            raise TypeError("Field type must be an output type.")
+        if args is None:
+            args = {}
+        elif not isinstance(args, dict):
+            raise TypeError("Field args must be a dict with argument names as keys.")
+        elif not all(
+            isinstance(value, GraphQLArgument) or is_input_type(value)
+            for value in args.values()
+        ):
+            raise TypeError(
+                "Field args must be GraphQLArguments or input type objects."
+            )
+        else:
+            args = {
+                name: value
+                if isinstance(value, GraphQLArgument)
+                else GraphQLArgument(cast(GraphQLInputType, value))
+                for name, value in args.items()
+            }
+        if resolve is not None and not callable(resolve):
+            raise TypeError(
+                "Field resolver must be a function if provided, "
+                f" but got: {inspect(resolve)}."
+            )
+        if description is not None and not is_description(description):
+            raise TypeError("The description must be a string.")
+        if deprecation_reason is not None and not is_description(deprecation_reason):
+            raise TypeError("The deprecation reason must be a string.")
+        if extensions is not None and (
+            not isinstance(extensions, dict)
+            or not all(isinstance(key, str) for key in extensions)
+        ):
+            raise TypeError("Field extensions must be a dictionary with string keys.")
+        if ast_node and not isinstance(ast_node, FieldDefinitionNode):
+            raise TypeError("Field AST node must be a FieldDefinitionNode.")
+        self.type = type_
+        self.args = args or {}
+        self.resolve = resolve
+        self.subscribe = subscribe
+        self.description = description
+        self.deprecation_reason = deprecation_reason
+        self.extensions = extensions
+        self.ast_node = ast_node
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__} {self.type!r}>"
+
+    def __str__(self) -> str:
+        return f"Field: {self.type}"
+
+    def __eq__(self, other: Any) -> bool:
+        return self is other or (
+            isinstance(other, GraphQLField)
+            and self.type == other.type
+            and self.args == other.args
+            and self.resolve == other.resolve
+            and self.description == other.description
+            and self.deprecation_reason == other.deprecation_reason
+            and self.extensions == other.extensions
+        )
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            type_=self.type,
+            args=self.args.copy() if self.args else None,
+            resolve=self.resolve,
+            subscribe=self.subscribe,
+            deprecation_reason=self.deprecation_reason,
+            description=self.description,
+            extensions=self.extensions,
+            ast_node=self.ast_node,
+        )
+
+    @property
+    def is_deprecated(self) -> bool:
+        return self.deprecation_reason is not None
+
+
+class GraphQLResolveInfo(NamedTuple):
+    """Collection of information passed to the resolvers.
+
+    This is always passed as the first argument to the resolvers.
+
+    Note that contrary to the JavaScript implementation, the context (commonly used to
+    represent an authenticated user, or request-specific caches) is included here and
+    not passed as an additional argument.
+    """
+
+    field_name: str
+    field_nodes: List[FieldNode]
+    return_type: "GraphQLOutputType"
+    parent_type: "GraphQLObjectType"
+    path: Path
+    schema: "GraphQLSchema"
+    fragments: Dict[str, FragmentDefinitionNode]
+    root_value: Any
+    operation: OperationDefinitionNode
+    variable_values: Dict[str, Any]
+    context: Any
+    is_awaitable: Callable[[Any], bool]
+
+
+# Note: Contrary to the Javascript implementation of GraphQLFieldResolver,
+# the context is passed as part of the GraphQLResolveInfo and any arguments
+# are passed individually as keyword arguments.
+GraphQLFieldResolverWithoutArgs = Callable[[Any, GraphQLResolveInfo], Any]
+# Unfortunately there is currently no syntax to indicate optional or keyword
+# arguments in Python, so we also allow any other Callable as a workaround:
+GraphQLFieldResolver = Callable[..., Any]
+
+# Note: Contrary to the Javascript implementation of GraphQLTypeResolver,
+# the context is passed as part of the GraphQLResolveInfo:
+GraphQLTypeResolver = Callable[
+    [Any, GraphQLResolveInfo, "GraphQLAbstractType"],
+    AwaitableOrValue[Optional[Union["GraphQLObjectType", str]]],
+]
+
+# Note: Contrary to the Javascript implementation of GraphQLIsTypeOfFn,
+# the context is passed as part of the GraphQLResolveInfo:
+GraphQLIsTypeOfFn = Callable[[Any, GraphQLResolveInfo], AwaitableOrValue[bool]]
+
+
+class GraphQLArgument:
+    """Definition of a GraphQL argument"""
+
+    type: "GraphQLInputType"
+    default_value: Any
+    description: Optional[str]
+    out_name: Optional[str]  # for transforming names (extension of GraphQL.js)
+    extensions: Optional[Dict[str, Any]]
+    ast_node: Optional[InputValueDefinitionNode]
+
+    def __init__(
+        self,
+        type_: "GraphQLInputType",
+        default_value: Any = Undefined,
+        description: Optional[str] = None,
+        out_name: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[InputValueDefinitionNode] = None,
+    ) -> None:
+        if not is_input_type(type_):
+            raise TypeError("Argument type must be a GraphQL input type.")
+        if description is not None and not is_description(description):
+            raise TypeError("Argument description must be a string.")
+        if out_name is not None and not isinstance(out_name, str):
+            raise TypeError("Argument out name must be a string.")
+        if extensions is not None and (
+            not isinstance(extensions, dict)
+            or not all(isinstance(key, str) for key in extensions)
+        ):
+            raise TypeError(
+                "Argument extensions must be a dictionary with string keys."
+            )
+        if ast_node and not isinstance(ast_node, InputValueDefinitionNode):
+            raise TypeError("Argument AST node must be an InputValueDefinitionNode.")
+        self.type = type_
+        self.default_value = default_value
+        self.description = description
+        self.out_name = out_name
+        self.extensions = extensions
+        self.ast_node = ast_node
+
+    def __eq__(self, other: Any) -> bool:
+        return self is other or (
+            isinstance(other, GraphQLArgument)
+            and self.type == other.type
+            and self.default_value == other.default_value
+            and self.description == other.description
+            and self.out_name == other.out_name
+            and self.extensions == other.extensions
+        )
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            type_=self.type,
+            default_value=self.default_value,
+            description=self.description,
+            out_name=self.out_name,
+            extensions=self.extensions,
+            ast_node=self.ast_node,
+        )
+
+
+def is_required_argument(arg: GraphQLArgument) -> bool:
+    return is_non_null_type(arg.type) and arg.default_value is Undefined
+
+
+T = TypeVar("T")
+Thunk = Union[Callable[[], T], T]
+
+GraphQLFieldMap = Dict[str, GraphQLField]
+
+
+class GraphQLObjectType(GraphQLNamedType):
+    """Object Type Definition
+
+    Almost all of the GraphQL types you define will be object types. Object types have
+    a name, but most importantly describe their fields.
+
+    Example::
+
+        AddressType = GraphQLObjectType('Address', {
+            'street': GraphQLField(GraphQLString),
+            'number': GraphQLField(GraphQLInt),
+            'formatted': GraphQLField(GraphQLString,
+                lambda obj, info, **args: f'{obj.number} {obj.street}')
+        })
+
+    When two types need to refer to each other, or a type needs to refer to itself in
+    a field, you can use a lambda function with no arguments (a so-called "thunk")
+    to supply the fields lazily.
+
+    Example::
+
+        PersonType = GraphQLObjectType('Person', lambda: {
+            'name': GraphQLField(GraphQLString),
+            'bestFriend': GraphQLField(PersonType)
+        })
+
+    """
+
+    is_type_of: Optional[GraphQLIsTypeOfFn]
+    ast_node: Optional[ObjectTypeDefinitionNode]
+    extension_ast_nodes: Optional[FrozenList[ObjectTypeExtensionNode]]
+
+    def __init__(
+        self,
+        name: str,
+        fields: Thunk[GraphQLFieldMap],
+        interfaces: Optional[Thunk[Collection["GraphQLInterfaceType"]]] = None,
+        is_type_of: Optional[GraphQLIsTypeOfFn] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        description: Optional[str] = None,
+        ast_node: Optional[ObjectTypeDefinitionNode] = None,
+        extension_ast_nodes: Optional[Collection[ObjectTypeExtensionNode]] = None,
+    ) -> None:
+        super().__init__(
+            name=name,
+            description=description,
+            extensions=extensions,
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        if is_type_of is not None and not callable(is_type_of):
+            raise TypeError(
+                f"{name} must provide 'is_type_of' as a function,"
+                f" but got: {inspect(is_type_of)}."
+            )
+        if ast_node and not isinstance(ast_node, ObjectTypeDefinitionNode):
+            raise TypeError(f"{name} AST node must be an ObjectTypeDefinitionNode.")
+        if extension_ast_nodes and not all(
+            isinstance(node, ObjectTypeExtensionNode) for node in extension_ast_nodes
+        ):
+            raise TypeError(
+                f"{name} extension AST nodes must be specified"
+                " as a collection of ObjectTypeExtensionNode instances."
+            )
+        self._fields = fields
+        self._interfaces = interfaces
+        self.is_type_of = is_type_of
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            **super().to_kwargs(),
+            fields=self.fields.copy(),
+            interfaces=self.interfaces,
+            is_type_of=self.is_type_of,
+        )
+
+    @cached_property
+    def fields(self) -> GraphQLFieldMap:
+        """Get provided fields, wrapping them as GraphQLFields if needed."""
+        try:
+            fields = resolve_thunk(self._fields)
+        except Exception as error:
+            raise TypeError(f"{self.name} fields cannot be resolved. {error}")
+        if not isinstance(fields, dict) or not all(
+            isinstance(key, str) for key in fields
+        ):
+            raise TypeError(
+                f"{self.name} fields must be specified"
+                " as a dict with field names as keys."
+            )
+        if not all(
+            isinstance(value, GraphQLField) or is_output_type(value)
+            for value in fields.values()
+        ):
+            raise TypeError(
+                f"{self.name} fields must be GraphQLField or output type objects."
+            )
+        return {
+            name: value if isinstance(value, GraphQLField) else GraphQLField(value)
+            for name, value in fields.items()
+        }
+
+    @cached_property
+    def interfaces(self) -> List["GraphQLInterfaceType"]:
+        """Get provided interfaces."""
+        try:
+            interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk(
+                self._interfaces
+            )
+        except Exception as error:
+            raise TypeError(f"{self.name} interfaces cannot be resolved. {error}")
+        if interfaces is None:
+            interfaces = []
+        elif not is_collection(interfaces) or not all(
+            isinstance(value, GraphQLInterfaceType) for value in interfaces
+        ):
+            raise TypeError(
+                f"{self.name} interfaces must be specified"
+                " as a collection of GraphQLInterfaceType instances."
+            )
+        return list(interfaces)
+
+
+def is_object_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLObjectType)
+
+
+def assert_object_type(type_: Any) -> GraphQLObjectType:
+    if not is_object_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL Object type.")
+    return cast(GraphQLObjectType, type_)
+
+
+class GraphQLInterfaceType(GraphQLNamedType):
+    """Interface Type Definition
+
+    When a field can return one of a heterogeneous set of types, an Interface type
+    is used to describe what types are possible, what fields are in common across
+    all types, as well as a function to determine which type is actually used when
+    the field is resolved.
+
+    Example::
+
+        EntityType = GraphQLInterfaceType('Entity', {
+                'name': GraphQLField(GraphQLString),
+            })
+    """
+
+    resolve_type: Optional[GraphQLTypeResolver]
+    ast_node: Optional[InterfaceTypeDefinitionNode]
+    extension_ast_nodes: Optional[FrozenList[InterfaceTypeExtensionNode]]
+
+    def __init__(
+        self,
+        name: str,
+        fields: Optional[Thunk[GraphQLFieldMap]] = None,
+        interfaces: Optional[Thunk[Collection["GraphQLInterfaceType"]]] = None,
+        resolve_type: Optional[GraphQLTypeResolver] = None,
+        description: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[InterfaceTypeDefinitionNode] = None,
+        extension_ast_nodes: Optional[Collection[InterfaceTypeExtensionNode]] = None,
+    ) -> None:
+        super().__init__(
+            name=name,
+            description=description,
+            extensions=extensions,
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        if resolve_type is not None and not callable(resolve_type):
+            raise TypeError(
+                f"{name} must provide 'resolve_type' as a function,"
+                f" but got: {inspect(resolve_type)}."
+            )
+        if ast_node and not isinstance(ast_node, InterfaceTypeDefinitionNode):
+            raise TypeError(f"{name} AST node must be an InterfaceTypeDefinitionNode.")
+        if extension_ast_nodes and not all(
+            isinstance(node, InterfaceTypeExtensionNode) for node in extension_ast_nodes
+        ):
+            raise TypeError(
+                f"{name} extension AST nodes must be specified"
+                " as a collection of InterfaceTypeExtensionNode instances."
+            )
+        self._fields = fields
+        self._interfaces = interfaces
+        self.resolve_type = resolve_type
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            **super().to_kwargs(),
+            fields=self.fields.copy(),
+            interfaces=self.interfaces.copy(),
+            resolve_type=self.resolve_type,
+        )
+
+    @cached_property
+    def fields(self) -> GraphQLFieldMap:
+        """Get provided fields, wrapping them as GraphQLFields if needed."""
+        try:
+            fields = resolve_thunk(self._fields)
+        except Exception as error:
+            raise TypeError(f"{self.name} fields cannot be resolved. {error}")
+        if not isinstance(fields, dict) or not all(
+            isinstance(key, str) for key in fields
+        ):
+            raise TypeError(
+                f"{self.name} fields must be specified"
+                " as a dict with field names as keys."
+            )
+        if not all(
+            isinstance(value, GraphQLField) or is_output_type(value)
+            for value in fields.values()
+        ):
+            raise TypeError(
+                f"{self.name} fields must be GraphQLField or output type objects."
+            )
+        return {
+            name: value if isinstance(value, GraphQLField) else GraphQLField(value)
+            for name, value in fields.items()
+        }
+
+    @cached_property
+    def interfaces(self) -> List["GraphQLInterfaceType"]:
+        """Get provided interfaces."""
+        try:
+            interfaces: Collection["GraphQLInterfaceType"] = resolve_thunk(
+                self._interfaces
+            )
+        except Exception as error:
+            raise TypeError(f"{self.name} interfaces cannot be resolved. {error}")
+        if interfaces is None:
+            interfaces = []
+        elif not is_collection(interfaces) or not all(
+            isinstance(value, GraphQLInterfaceType) for value in interfaces
+        ):
+            raise TypeError(
+                f"{self.name} interfaces must be specified"
+                " as a collection of GraphQLInterfaceType instances."
+            )
+        return list(interfaces)
+
+
+def is_interface_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLInterfaceType)
+
+
+def assert_interface_type(type_: Any) -> GraphQLInterfaceType:
+    if not is_interface_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL Interface type.")
+    return cast(GraphQLInterfaceType, type_)
+
+
+class GraphQLUnionType(GraphQLNamedType):
+    """Union Type Definition
+
+    When a field can return one of a heterogeneous set of types, a Union type is used
+    to describe what types are possible as well as providing a function to determine
+    which type is actually used when the field is resolved.
+
+    Example::
+
+        class PetType(GraphQLUnionType):
+            name = 'Pet'
+            types = [DogType, CatType]
+
+            def resolve_type(self, value, _type):
+                if isinstance(value, Dog):
+                    return DogType()
+                if isinstance(value, Cat):
+                    return CatType()
+    """
+
+    resolve_type: Optional[GraphQLTypeResolver]
+    ast_node: Optional[UnionTypeDefinitionNode]
+    extension_ast_nodes: Optional[FrozenList[UnionTypeExtensionNode]]
+
+    def __init__(
+        self,
+        name: str,
+        types: Thunk[Collection[GraphQLObjectType]],
+        resolve_type: Optional[GraphQLTypeResolver] = None,
+        description: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[UnionTypeDefinitionNode] = None,
+        extension_ast_nodes: Optional[Collection[UnionTypeExtensionNode]] = None,
+    ) -> None:
+        """
+
+        :rtype: object
+        """
+        super().__init__(
+            name=name,
+            description=description,
+            extensions=extensions,
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        if resolve_type is not None and not callable(resolve_type):
+            raise TypeError(
+                f"{name} must provide 'resolve_type' as a function,"
+                f" but got: {inspect(resolve_type)}."
+            )
+        if ast_node and not isinstance(ast_node, UnionTypeDefinitionNode):
+            raise TypeError(f"{name} AST node must be a UnionTypeDefinitionNode.")
+        if extension_ast_nodes and not all(
+            isinstance(node, UnionTypeExtensionNode) for node in extension_ast_nodes
+        ):
+            raise TypeError(
+                f"{name} extension AST nodes must be specified"
+                " as a collection of UnionTypeExtensionNode instances."
+            )
+        self._types = types
+        self.resolve_type = resolve_type
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            **super().to_kwargs(), types=self.types, resolve_type=self.resolve_type
+        )
+
+    @cached_property
+    def types(self) -> List[GraphQLObjectType]:
+        """Get provided types."""
+        try:
+            types: Collection[GraphQLObjectType] = resolve_thunk(self._types)
+        except Exception as error:
+            raise TypeError(f"{self.name} types cannot be resolved. {error}")
+        if types is None:
+            types = []
+        elif not is_collection(types) or not all(
+            isinstance(value, GraphQLObjectType) for value in types
+        ):
+            raise TypeError(
+                f"{self.name} types must be specified"
+                " as a collection of GraphQLObjectType instances."
+            )
+        return list(types)
+
+
+def is_union_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLUnionType)
+
+
+def assert_union_type(type_: Any) -> GraphQLUnionType:
+    if not is_union_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL Union type.")
+    return cast(GraphQLUnionType, type_)
+
+
+GraphQLEnumValueMap = Dict[str, "GraphQLEnumValue"]
+
+
+class GraphQLEnumType(GraphQLNamedType):
+    """Enum Type Definition
+
+    Some leaf values of requests and input values are Enums. GraphQL serializes Enum
+    values as strings, however internally Enums can be represented by any kind of type,
+    often integers. They can also be provided as a Python Enum.
+
+    Example::
+
+        RGBType = GraphQLEnumType('RGB', {
+            'RED': 0,
+            'GREEN': 1,
+            'BLUE': 2
+        })
+
+    Example using a Python Enum::
+
+        class RGBEnum(enum.Enum):
+            RED = 0
+            GREEN = 1
+            BLUE = 2
+
+        RGBType = GraphQLEnumType('RGB', enum.Enum)
+
+    Instead of raw values, you can also specify GraphQLEnumValue objects with more
+    detail like description or deprecation information.
+
+    Note: If a value is not provided in a definition, the name of the enum value will
+    be used as its internal value when the value is serialized.
+    """
+
+    values: GraphQLEnumValueMap
+    ast_node: Optional[EnumTypeDefinitionNode]
+    extension_ast_nodes: Optional[FrozenList[EnumTypeExtensionNode]]
+
+    def __init__(
+        self,
+        name: str,
+        values: Union[GraphQLEnumValueMap, Dict[str, Any], Type[Enum]],
+        description: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[EnumTypeDefinitionNode] = None,
+        extension_ast_nodes: Optional[Collection[EnumTypeExtensionNode]] = None,
+    ) -> None:
+        super().__init__(
+            name=name,
+            description=description,
+            extensions=extensions,
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        try:  # check for enum
+            values = cast(Enum, values).__members__  # type: ignore
+        except AttributeError:
+            if not isinstance(values, dict) or not all(
+                isinstance(name, str) for name in values
+            ):
+                try:
+                    # noinspection PyTypeChecker
+                    values = dict(values)  # type: ignore
+                except (TypeError, ValueError):
+                    raise TypeError(
+                        f"{name} values must be an Enum or a dict"
+                        " with value names as keys."
+                    )
+            values = cast(Dict, values)
+        else:
+            values = cast(Dict, values)
+            values = {key: value.value for key, value in values.items()}
+        values = {
+            key: value
+            if isinstance(value, GraphQLEnumValue)
+            else GraphQLEnumValue(value)
+            for key, value in values.items()
+        }
+        if ast_node and not isinstance(ast_node, EnumTypeDefinitionNode):
+            raise TypeError(f"{name} AST node must be an EnumTypeDefinitionNode.")
+        if extension_ast_nodes and not all(
+            isinstance(node, EnumTypeExtensionNode) for node in extension_ast_nodes
+        ):
+            raise TypeError(
+                f"{name} extension AST nodes must be specified"
+                " as a collection of EnumTypeExtensionNode instances."
+            )
+        self.values = values
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(**super().to_kwargs(), values=self.values.copy())
+
+    @cached_property
+    def _value_lookup(self) -> Dict[Any, str]:
+        # use first value or name as lookup
+        lookup: Dict[Any, str] = {}
+        for name, enum_value in self.values.items():
+            value = enum_value.value
+            if value is None or value is Undefined:
+                value = name
+            try:
+                if value not in lookup:
+                    lookup[value] = name
+            except TypeError:
+                pass  # ignore unhashable values
+        return lookup
+
+    def serialize(self, output_value: Any) -> str:
+        try:
+            return self._value_lookup[output_value]
+        except KeyError:  # hashable value not found
+            pass
+        except TypeError:  # unhashable value, we need to scan all values
+            for enum_name, enum_value in self.values.items():
+                if enum_value.value == output_value:
+                    return enum_name
+        raise GraphQLError(
+            f"Enum '{self.name}' cannot represent value: {inspect(output_value)}"
+        )
+
+    def parse_value(self, input_value: str) -> Any:
+        if isinstance(input_value, str):
+            try:
+                enum_value = self.values[input_value]
+            except KeyError:
+                raise GraphQLError(
+                    f"Value '{input_value}' does not exist in '{self.name}' enum."
+                    + did_you_mean_enum_value(self, input_value)
+                )
+            return enum_value.value
+        value_str = inspect(input_value)
+        raise GraphQLError(
+            f"Enum '{self.name}' cannot represent non-string value: {value_str}."
+            + did_you_mean_enum_value(self, value_str)
+        )
+
+    def parse_literal(
+        self, value_node: ValueNode, _variables: Optional[Dict[str, Any]] = None
+    ) -> Any:
+        # Note: variables will be resolved before calling this method.
+        if isinstance(value_node, EnumValueNode):
+            try:
+                enum_value = self.values[value_node.value]
+            except KeyError:
+                value_str = print_ast(value_node)
+                raise GraphQLError(
+                    f"Value '{value_str}' does not exist in '{self.name}' enum."
+                    + did_you_mean_enum_value(self, value_str),
+                    value_node,
+                )
+            return enum_value.value
+        value_str = print_ast(value_node)
+        raise GraphQLError(
+            f"Enum '{self.name}' cannot represent non-enum value: {value_str}."
+            + did_you_mean_enum_value(self, value_str),
+            value_node,
+        )
+
+
+def is_enum_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLEnumType)
+
+
+def assert_enum_type(type_: Any) -> GraphQLEnumType:
+    if not is_enum_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL Enum type.")
+    return cast(GraphQLEnumType, type_)
+
+
+def did_you_mean_enum_value(enum_type: GraphQLEnumType, unknown_value_str: str) -> str:
+    suggested_values = suggestion_list(unknown_value_str, enum_type.values)
+    return did_you_mean(suggested_values, "the enum value")
+
+
+class GraphQLEnumValue:
+
+    value: Any
+    description: Optional[str]
+    deprecation_reason: Optional[str]
+    extensions: Optional[Dict[str, Any]]
+    ast_node: Optional[EnumValueDefinitionNode]
+
+    def __init__(
+        self,
+        value: Any = None,
+        description: Optional[str] = None,
+        deprecation_reason: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[EnumValueDefinitionNode] = None,
+    ) -> None:
+        if description is not None and not is_description(description):
+            raise TypeError("The description of the enum value must be a string.")
+        if deprecation_reason is not None and not is_description(deprecation_reason):
+            raise TypeError(
+                "The deprecation reason for the enum value must be a string."
+            )
+        if extensions is not None and (
+            not isinstance(extensions, dict)
+            or not all(isinstance(key, str) for key in extensions)
+        ):
+            raise TypeError(
+                "Enum value extensions must be a dictionary with string keys."
+            )
+        if ast_node and not isinstance(ast_node, EnumValueDefinitionNode):
+            raise TypeError("AST node must be an EnumValueDefinitionNode.")
+        self.value = value
+        self.description = description
+        self.deprecation_reason = deprecation_reason
+        self.extensions = extensions
+        self.ast_node = ast_node
+
+    def __eq__(self, other: Any) -> bool:
+        return self is other or (
+            isinstance(other, GraphQLEnumValue)
+            and self.value == other.value
+            and self.description == other.description
+            and self.deprecation_reason == other.deprecation_reason
+            and self.extensions == other.extensions
+        )
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            value=self.value,
+            description=self.description,
+            deprecation_reason=self.deprecation_reason,
+            extensions=self.extensions,
+            ast_node=self.ast_node,
+        )
+
+    @property
+    def is_deprecated(self) -> bool:
+        return self.deprecation_reason is not None
+
+
+GraphQLInputFieldMap = Dict[str, "GraphQLInputField"]
+GraphQLInputFieldOutType = Callable[[Dict[str, Any]], Any]
+
+
+class GraphQLInputObjectType(GraphQLNamedType):
+    """Input Object Type Definition
+
+    An input object defines a structured collection of fields which may be supplied
+    to a field argument.
+
+    Using ``NonNull`` will ensure that a value must be provided by the query.
+
+    Example::
+
+        NonNullFloat = GraphQLNonNull(GraphQLFloat())
+
+        class GeoPoint(GraphQLInputObjectType):
+            name = 'GeoPoint'
+            fields = {
+                'lat': GraphQLInputField(NonNullFloat),
+                'lon': GraphQLInputField(NonNullFloat),
+                'alt': GraphQLInputField(
+                          GraphQLFloat(), default_value=0)
+            }
+
+    The outbound values will be Python dictionaries by default, but you can have them
+    converted to other types by specifying an ``out_type`` function or class.
+    """
+
+    ast_node: Optional[InputObjectTypeDefinitionNode]
+    extension_ast_nodes: Optional[FrozenList[InputObjectTypeExtensionNode]]
+
+    def __init__(
+        self,
+        name: str,
+        fields: Thunk[GraphQLInputFieldMap],
+        description: Optional[str] = None,
+        out_type: Optional[GraphQLInputFieldOutType] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[InputObjectTypeDefinitionNode] = None,
+        extension_ast_nodes: Optional[Collection[InputObjectTypeExtensionNode]] = None,
+    ) -> None:
+        super().__init__(
+            name=name,
+            description=description,
+            extensions=extensions,
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        if out_type is not None and not callable(out_type):
+            raise TypeError(f"The out type for {name} must be a function or a class.")
+        if ast_node and not isinstance(ast_node, InputObjectTypeDefinitionNode):
+            raise TypeError(
+                f"{name} AST node must be an InputObjectTypeDefinitionNode."
+            )
+        if extension_ast_nodes and not all(
+            isinstance(node, InputObjectTypeExtensionNode)
+            for node in extension_ast_nodes
+        ):
+            raise TypeError(
+                f"{name} extension AST nodes must be specified"
+                " as a collection of InputObjectTypeExtensionNode instances."
+            )
+        self._fields = fields
+        if out_type is not None:
+            self.out_type = out_type  # type: ignore
+
+    @staticmethod
+    def out_type(value: Dict[str, Any]) -> Any:
+        """Transform outbound values (this is an extension of GraphQL.js).
+
+        This default implementation passes values unaltered as dictionaries.
+        """
+        return value
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            **super().to_kwargs(),
+            fields=self.fields.copy(),
+            out_type=None
+            if self.out_type is GraphQLInputObjectType.out_type
+            else self.out_type,
+        )
+
+    @cached_property
+    def fields(self) -> GraphQLInputFieldMap:
+        """Get provided fields, wrap them as GraphQLInputField if needed."""
+        try:
+            fields = resolve_thunk(self._fields)
+        except Exception as error:
+            raise TypeError(f"{self.name} fields cannot be resolved. {error}")
+        if not isinstance(fields, dict) or not all(
+            isinstance(key, str) for key in fields
+        ):
+            raise TypeError(
+                f"{self.name} fields must be specified"
+                " as a dict with field names as keys."
+            )
+        if not all(
+            isinstance(value, GraphQLInputField) or is_input_type(value)
+            for value in fields.values()
+        ):
+            raise TypeError(
+                f"{self.name} fields must be"
+                " GraphQLInputField or input type objects."
+            )
+        return {
+            name: value
+            if isinstance(value, GraphQLInputField)
+            else GraphQLInputField(value)
+            for name, value in fields.items()
+        }
+
+
+def is_input_object_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLInputObjectType)
+
+
+def assert_input_object_type(type_: Any) -> GraphQLInputObjectType:
+    if not is_input_object_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL Input Object type.")
+    return cast(GraphQLInputObjectType, type_)
+
+
+class GraphQLInputField:
+    """Definition of a GraphQL input field"""
+
+    type: "GraphQLInputType"
+    default_value: Any
+    description: Optional[str]
+    out_name: Optional[str]  # for transforming names (extension of GraphQL.js)
+    extensions: Optional[Dict[str, Any]]
+    ast_node: Optional[InputValueDefinitionNode]
+
+    def __init__(
+        self,
+        type_: "GraphQLInputType",
+        default_value: Any = Undefined,
+        description: Optional[str] = None,
+        out_name: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[InputValueDefinitionNode] = None,
+    ) -> None:
+        if not is_input_type(type_):
+            raise TypeError("Input field type must be a GraphQL input type.")
+        if description is not None and not is_description(description):
+            raise TypeError("Input field description must be a string.")
+        if out_name is not None and not isinstance(out_name, str):
+            raise TypeError("Input field out name must be a string.")
+        if extensions is not None and (
+            not isinstance(extensions, dict)
+            or not all(isinstance(key, str) for key in extensions)
+        ):
+            raise TypeError(
+                "Input field extensions must be a dictionary with string keys."
+            )
+        if ast_node and not isinstance(ast_node, InputValueDefinitionNode):
+            raise TypeError("Input field AST node must be an InputValueDefinitionNode.")
+        self.type = type_
+        self.default_value = default_value
+        self.description = description
+        self.out_name = out_name
+        self.extensions = extensions
+        self.ast_node = ast_node
+
+    def __eq__(self, other: Any) -> bool:
+        return self is other or (
+            isinstance(other, GraphQLInputField)
+            and self.type == other.type
+            and self.default_value == other.default_value
+            and self.description == other.description
+            and self.extensions == other.extensions
+            and self.out_name == other.out_name
+        )
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            type_=self.type,
+            default_value=self.default_value,
+            description=self.description,
+            out_name=self.out_name,
+            extensions=self.extensions,
+            ast_node=self.ast_node,
+        )
+
+
+def is_required_input_field(field: GraphQLInputField) -> bool:
+    return is_non_null_type(field.type) and field.default_value is Undefined
+
+
+# Wrapper types
+
+
+class GraphQLList(Generic[GT], GraphQLWrappingType[GT]):
+    """List Type Wrapper
+
+    A list is a wrapping type which points to another type. Lists are often created
+    within the context of defining the fields of an object type.
+
+    Example::
+
+        class PersonType(GraphQLObjectType):
+            name = 'Person'
+
+            @property
+            def fields(self):
+                return {
+                    'parents': GraphQLField(GraphQLList(PersonType())),
+                    'children': GraphQLField(GraphQLList(PersonType())),
+                }
+    """
+
+    def __init__(self, type_: GT) -> None:
+        super().__init__(type_=type_)
+
+    def __str__(self) -> str:
+        return f"[{self.of_type}]"
+
+
+def is_list_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLList)
+
+
+def assert_list_type(type_: Any) -> GraphQLList:
+    if not is_list_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL List type.")
+    return cast(GraphQLList, type_)
+
+
+GNT = TypeVar("GNT", bound="GraphQLNullableType")
+
+
+class GraphQLNonNull(GraphQLWrappingType[GNT], Generic[GNT]):
+    """Non-Null Type Wrapper
+
+    A non-null is a wrapping type which points to another type. Non-null types enforce
+    that their values are never null and can ensure an error is raised if this ever
+    occurs during a request. It is useful for fields which you can make a strong
+    guarantee on non-nullability, for example usually the id field of a database row
+    will never be null.
+
+    Example::
+
+        class RowType(GraphQLObjectType):
+            name = 'Row'
+            fields = {
+                'id': GraphQLField(GraphQLNonNull(GraphQLString()))
+            }
+
+    Note: the enforcement of non-nullability occurs within the executor.
+    """
+
+    def __init__(self, type_: GNT):
+        super().__init__(type_=type_)
+        if isinstance(type_, GraphQLNonNull):
+            raise TypeError(
+                "Can only create NonNull of a Nullable GraphQLType but got:"
+                f" {type_}."
+            )
+
+    def __str__(self) -> str:
+        return f"{self.of_type}!"
+
+
+def is_non_null_type(type_: Any) -> bool:
+    return isinstance(type_, GraphQLNonNull)
+
+
+def assert_non_null_type(type_: Any) -> GraphQLNonNull:
+    if not is_non_null_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL Non-Null type.")
+    return cast(GraphQLNonNull, type_)
+
+
+# These types can all accept null as a value.
+
+graphql_nullable_types = (
+    GraphQLScalarType,
+    GraphQLObjectType,
+    GraphQLInterfaceType,
+    GraphQLUnionType,
+    GraphQLEnumType,
+    GraphQLInputObjectType,
+    GraphQLList,
+)
+
+GraphQLNullableType = Union[
+    GraphQLScalarType,
+    GraphQLObjectType,
+    GraphQLInterfaceType,
+    GraphQLUnionType,
+    GraphQLEnumType,
+    GraphQLInputObjectType,
+    GraphQLList,
+]
+
+
+def is_nullable_type(type_: Any) -> bool:
+    return isinstance(type_, graphql_nullable_types)
+
+
+def assert_nullable_type(type_: Any) -> GraphQLNullableType:
+    if not is_nullable_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL nullable type.")
+    return cast(GraphQLNullableType, type_)
+
+
+@overload
+def get_nullable_type(type_: None) -> None:
+    ...
+
+
+@overload
+def get_nullable_type(type_: GraphQLNullableType) -> GraphQLNullableType:
+    ...
+
+
+@overload
+def get_nullable_type(type_: GraphQLNonNull) -> GraphQLNullableType:
+    ...
+
+
+def get_nullable_type(
+    type_: Optional[Union[GraphQLNullableType, GraphQLNonNull]]
+) -> Optional[GraphQLNullableType]:
+    """Unwrap possible non-null type"""
+    if is_non_null_type(type_):
+        type_ = cast(GraphQLNonNull, type_)
+        type_ = type_.of_type
+    return cast(Optional[GraphQLNullableType], type_)
+
+
+# These types may be used as input types for arguments and directives.
+
+graphql_input_types = (GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType)
+
+GraphQLInputType = Union[
+    GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType, GraphQLWrappingType
+]
+
+
+def is_input_type(type_: Any) -> bool:
+    return isinstance(type_, graphql_input_types) or (
+        isinstance(type_, GraphQLWrappingType) and is_input_type(type_.of_type)
+    )
+
+
+def assert_input_type(type_: Any) -> GraphQLInputType:
+    if not is_input_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL input type.")
+    return cast(GraphQLInputType, type_)
+
+
+# These types may be used as output types as the result of fields.
+
+graphql_output_types = (
+    GraphQLScalarType,
+    GraphQLObjectType,
+    GraphQLInterfaceType,
+    GraphQLUnionType,
+    GraphQLEnumType,
+)
+
+GraphQLOutputType = Union[
+    GraphQLScalarType,
+    GraphQLObjectType,
+    GraphQLInterfaceType,
+    GraphQLUnionType,
+    GraphQLEnumType,
+    GraphQLWrappingType,
+]
+
+
+def is_output_type(type_: Any) -> bool:
+    return isinstance(type_, graphql_output_types) or (
+        isinstance(type_, GraphQLWrappingType) and is_output_type(type_.of_type)
+    )
+
+
+def assert_output_type(type_: Any) -> GraphQLOutputType:
+    if not is_output_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL output type.")
+    return cast(GraphQLOutputType, type_)
+
+
+# These types may describe types which may be leaf values.
+
+graphql_leaf_types = (GraphQLScalarType, GraphQLEnumType)
+
+GraphQLLeafType = Union[GraphQLScalarType, GraphQLEnumType]
+
+
+def is_leaf_type(type_: Any) -> bool:
+    return isinstance(type_, graphql_leaf_types)
+
+
+def assert_leaf_type(type_: Any) -> GraphQLLeafType:
+    if not is_leaf_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL leaf type.")
+    return cast(GraphQLLeafType, type_)
+
+
+# These types may describe the parent context of a selection set.
+
+graphql_composite_types = (GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType)
+
+GraphQLCompositeType = Union[GraphQLObjectType, GraphQLInterfaceType, GraphQLUnionType]
+
+
+def is_composite_type(type_: Any) -> bool:
+    return isinstance(type_, graphql_composite_types)
+
+
+def assert_composite_type(type_: Any) -> GraphQLType:
+    if not is_composite_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL composite type.")
+    return cast(GraphQLType, type_)
+
+
+# These types may describe abstract types.
+
+graphql_abstract_types = (GraphQLInterfaceType, GraphQLUnionType)
+
+GraphQLAbstractType = Union[GraphQLInterfaceType, GraphQLUnionType]
+
+
+def is_abstract_type(type_: Any) -> bool:
+    return isinstance(type_, graphql_abstract_types)
+
+
+def assert_abstract_type(type_: Any) -> GraphQLAbstractType:
+    if not is_abstract_type(type_):
+        raise TypeError(f"Expected {type_} to be a GraphQL composite type.")
+    return cast(GraphQLAbstractType, type_)
diff --git a/src/graphql/type/directives.py b/src/graphql/type/directives.py
new file mode 100644
index 0000000..794aa4b
--- /dev/null
+++ b/src/graphql/type/directives.py
@@ -0,0 +1,230 @@
+from typing import Any, Collection, Dict, List, Optional, cast
+
+from ..language import ast, DirectiveLocation
+from ..pyutils import inspect, is_description, FrozenList
+from .definition import GraphQLArgument, GraphQLInputType, GraphQLNonNull, is_input_type
+from .scalars import GraphQLBoolean, GraphQLString
+
+__all__ = [
+    "is_directive",
+    "assert_directive",
+    "is_specified_directive",
+    "specified_directives",
+    "GraphQLDirective",
+    "GraphQLIncludeDirective",
+    "GraphQLSkipDirective",
+    "GraphQLDeprecatedDirective",
+    "GraphQLSpecifiedByDirective",
+    "DirectiveLocation",
+    "DEFAULT_DEPRECATION_REASON",
+]
+
+
+class GraphQLDirective:
+    """GraphQL Directive
+
+    Directives are used by the GraphQL runtime as a way of modifying execution behavior.
+    Type system creators will usually not create these directly.
+    """
+
+    name: str
+    locations: List[DirectiveLocation]
+    is_repeatable: bool
+    args: Dict[str, GraphQLArgument]
+    description: Optional[str]
+    extensions: Optional[Dict[str, Any]]
+    ast_node: Optional[ast.DirectiveDefinitionNode]
+
+    def __init__(
+        self,
+        name: str,
+        locations: Collection[DirectiveLocation],
+        args: Optional[Dict[str, GraphQLArgument]] = None,
+        is_repeatable: bool = False,
+        description: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[ast.DirectiveDefinitionNode] = None,
+    ) -> None:
+        if not name:
+            raise TypeError("Directive must be named.")
+        elif not isinstance(name, str):
+            raise TypeError("The directive name must be a string.")
+        try:
+            locations = [
+                value
+                if isinstance(value, DirectiveLocation)
+                else DirectiveLocation[cast(str, value)]
+                for value in locations
+            ]
+        except (KeyError, TypeError):
+            raise TypeError(
+                f"{name} locations must be specified"
+                " as a collection of DirectiveLocation enum values."
+            )
+        if args is None:
+            args = {}
+        elif not isinstance(args, dict) or not all(
+            isinstance(key, str) for key in args
+        ):
+            raise TypeError(f"{name} args must be a dict with argument names as keys.")
+        elif not all(
+            isinstance(value, GraphQLArgument) or is_input_type(value)
+            for value in args.values()
+        ):
+            raise TypeError(
+                f"{name} args must be GraphQLArgument or input type objects."
+            )
+        else:
+            args = {
+                name: value
+                if isinstance(value, GraphQLArgument)
+                else GraphQLArgument(cast(GraphQLInputType, value))
+                for name, value in args.items()
+            }
+        if not isinstance(is_repeatable, bool):
+            raise TypeError(f"{name} is_repeatable flag must be True or False.")
+        if ast_node and not isinstance(ast_node, ast.DirectiveDefinitionNode):
+            raise TypeError(f"{name} AST node must be a DirectiveDefinitionNode.")
+        if description is not None and not is_description(description):
+            raise TypeError(f"{name} description must be a string.")
+        if extensions is not None and (
+            not isinstance(extensions, dict)
+            or not all(isinstance(key, str) for key in extensions)
+        ):
+            raise TypeError(f"{name} extensions must be a dictionary with string keys.")
+        self.name = name
+        self.locations = locations
+        self.args = args
+        self.is_repeatable = is_repeatable
+        self.description = description
+        self.extensions = extensions
+        self.ast_node = ast_node
+
+    def __str__(self) -> str:
+        return f"@{self.name}"
+
+    def __repr__(self) -> str:
+        return f"<{self.__class__.__name__}({self})>"
+
+    def __eq__(self, other: Any) -> bool:
+        return self is other or (
+            isinstance(other, GraphQLDirective)
+            and self.name == other.name
+            and self.locations == other.locations
+            and self.args == other.args
+            and self.is_repeatable == other.is_repeatable
+            and self.description == other.description
+            and self.extensions == other.extensions
+        )
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            name=self.name,
+            locations=self.locations,
+            args=self.args,
+            is_repeatable=self.is_repeatable,
+            description=self.description,
+            extensions=self.extensions,
+            ast_node=self.ast_node,
+        )
+
+
+def is_directive(directive: Any) -> bool:
+    """Test if the given value is a GraphQL directive."""
+    return isinstance(directive, GraphQLDirective)
+
+
+def assert_directive(directive: Any) -> GraphQLDirective:
+    if not is_directive(directive):
+        raise TypeError(f"Expected {inspect(directive)} to be a GraphQL directive.")
+    return cast(GraphQLDirective, directive)
+
+
+# Used to conditionally include fields or fragments.
+GraphQLIncludeDirective = GraphQLDirective(
+    name="include",
+    locations=[
+        DirectiveLocation.FIELD,
+        DirectiveLocation.FRAGMENT_SPREAD,
+        DirectiveLocation.INLINE_FRAGMENT,
+    ],
+    args={
+        "if": GraphQLArgument(
+            GraphQLNonNull(GraphQLBoolean), description="Included when true."
+        )
+    },
+    description="Directs the executor to include this field or fragment"
+    " only when the `if` argument is true.",
+)
+
+
+# Used to conditionally skip (exclude) fields or fragments:
+GraphQLSkipDirective = GraphQLDirective(
+    name="skip",
+    locations=[
+        DirectiveLocation.FIELD,
+        DirectiveLocation.FRAGMENT_SPREAD,
+        DirectiveLocation.INLINE_FRAGMENT,
+    ],
+    args={
+        "if": GraphQLArgument(
+            GraphQLNonNull(GraphQLBoolean), description="Skipped when true."
+        )
+    },
+    description="Directs the executor to skip this field or fragment"
+    " when the `if` argument is true.",
+)
+
+
+# Constant string used for default reason for a deprecation:
+DEFAULT_DEPRECATION_REASON = "No longer supported"
+
+# Used to declare element of a GraphQL schema as deprecated:
+GraphQLDeprecatedDirective = GraphQLDirective(
+    name="deprecated",
+    locations=[DirectiveLocation.FIELD_DEFINITION, DirectiveLocation.ENUM_VALUE],
+    args={
+        "reason": GraphQLArgument(
+            GraphQLString,
+            description="Explains why this element was deprecated,"
+            " usually also including a suggestion for how to access"
+            " supported similar data."
+            " Formatted using the Markdown syntax, as specified by"
+            " [CommonMark](https://commonmark.org/).",
+            default_value=DEFAULT_DEPRECATION_REASON,
+        )
+    },
+    description="Marks an element of a GraphQL schema as no longer supported.",
+)
+
+# Used to provide a URL for specifying the behaviour of custom scalar definitions:
+GraphQLSpecifiedByDirective = GraphQLDirective(
+    name="specifiedBy",
+    locations=[DirectiveLocation.SCALAR],
+    args={
+        "url": GraphQLArgument(
+            GraphQLNonNull(GraphQLString),
+            description="The URL that specifies the behaviour of this scalar.",
+        )
+    },
+    description="Exposes a URL that specifies the behaviour of this scalar.",
+)
+
+
+specified_directives: FrozenList[GraphQLDirective] = FrozenList(
+    [
+        GraphQLIncludeDirective,
+        GraphQLSkipDirective,
+        GraphQLDeprecatedDirective,
+        GraphQLSpecifiedByDirective,
+    ]
+)
+specified_directives.__doc__ = """The full list of specified directives."""
+
+
+def is_specified_directive(directive: GraphQLDirective) -> bool:
+    """Check whether the given directive is one of the specified directives."""
+    return any(
+        specified_directive.name == directive.name
+        for specified_directive in specified_directives
+    )
diff --git a/src/graphql/type/introspection.py b/src/graphql/type/introspection.py
new file mode 100644
index 0000000..6262b59
--- /dev/null
+++ b/src/graphql/type/introspection.py
@@ -0,0 +1,526 @@
+from enum import Enum
+
+from .definition import (
+    GraphQLArgument,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLList,
+    GraphQLNamedType,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    is_abstract_type,
+    is_enum_type,
+    is_input_object_type,
+    is_interface_type,
+    is_list_type,
+    is_non_null_type,
+    is_object_type,
+    is_scalar_type,
+    is_union_type,
+)
+from ..language import DirectiveLocation, print_ast
+from ..pyutils import inspect, FrozenDict
+from .scalars import GraphQLBoolean, GraphQLString
+
+__all__ = [
+    "SchemaMetaFieldDef",
+    "TypeKind",
+    "TypeMetaFieldDef",
+    "TypeNameMetaFieldDef",
+    "introspection_types",
+    "is_introspection_type",
+]
+
+
+__Schema: GraphQLObjectType = GraphQLObjectType(
+    name="__Schema",
+    description="A GraphQL Schema defines the capabilities of a GraphQL"
+    " server. It exposes all available types and directives"
+    " on the server, as well as the entry points for query,"
+    " mutation, and subscription operations.",
+    fields=lambda: {
+        "description": GraphQLField(
+            GraphQLString, resolve=lambda schema, _info: schema.description
+        ),
+        "types": GraphQLField(
+            GraphQLNonNull(GraphQLList(GraphQLNonNull(__Type))),
+            resolve=lambda schema, _info: schema.type_map.values(),
+            description="A list of all types supported by this server.",
+        ),
+        "queryType": GraphQLField(
+            GraphQLNonNull(__Type),
+            resolve=lambda schema, _info: schema.query_type,
+            description="The type that query operations will be rooted at.",
+        ),
+        "mutationType": GraphQLField(
+            __Type,
+            resolve=lambda schema, _info: schema.mutation_type,
+            description="If this server supports mutation, the type that"
+            " mutation operations will be rooted at.",
+        ),
+        "subscriptionType": GraphQLField(
+            __Type,
+            resolve=lambda schema, _info: schema.subscription_type,
+            description="If this server support subscription, the type that"
+            " subscription operations will be rooted at.",
+        ),
+        "directives": GraphQLField(
+            GraphQLNonNull(GraphQLList(GraphQLNonNull(__Directive))),
+            resolve=lambda schema, _info: schema.directives,
+            description="A list of all directives supported by this server.",
+        ),
+    },
+)
+
+
+__Directive: GraphQLObjectType = GraphQLObjectType(
+    name="__Directive",
+    description="A Directive provides a way to describe alternate runtime"
+    " execution and type validation behavior in a GraphQL"
+    " document.\n\nIn some cases, you need to provide options"
+    " to alter GraphQL's execution behavior in ways field"
+    " arguments will not suffice, such as conditionally including"
+    " or skipping a field. Directives provide this by describing"
+    " additional information to the executor.",
+    fields=lambda: {
+        # Note: The fields onOperation, onFragment and onField are deprecated
+        "name": GraphQLField(
+            GraphQLNonNull(GraphQLString),
+            resolve=lambda directive, _info: directive.name,
+        ),
+        "description": GraphQLField(
+            GraphQLString, resolve=lambda directive, _info: directive.description
+        ),
+        "isRepeatable": GraphQLField(
+            GraphQLNonNull(GraphQLBoolean),
+            resolve=lambda directive, _info: directive.is_repeatable,
+        ),
+        "locations": GraphQLField(
+            GraphQLNonNull(GraphQLList(GraphQLNonNull(__DirectiveLocation))),
+            resolve=lambda directive, _info: directive.locations,
+        ),
+        "args": GraphQLField(
+            GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))),
+            resolve=lambda directive, _info: directive.args.items(),
+        ),
+    },
+)
+
+
+__DirectiveLocation: GraphQLEnumType = GraphQLEnumType(
+    name="__DirectiveLocation",
+    description="A Directive can be adjacent to many parts of the GraphQL"
+    " language, a __DirectiveLocation describes one such possible"
+    " adjacencies.",
+    values={
+        "QUERY": GraphQLEnumValue(
+            DirectiveLocation.QUERY,
+            description="Location adjacent to a query operation.",
+        ),
+        "MUTATION": GraphQLEnumValue(
+            DirectiveLocation.MUTATION,
+            description="Location adjacent to a mutation operation.",
+        ),
+        "SUBSCRIPTION": GraphQLEnumValue(
+            DirectiveLocation.SUBSCRIPTION,
+            description="Location adjacent to a subscription operation.",
+        ),
+        "FIELD": GraphQLEnumValue(
+            DirectiveLocation.FIELD, description="Location adjacent to a field."
+        ),
+        "FRAGMENT_DEFINITION": GraphQLEnumValue(
+            DirectiveLocation.FRAGMENT_DEFINITION,
+            description="Location adjacent to a fragment definition.",
+        ),
+        "FRAGMENT_SPREAD": GraphQLEnumValue(
+            DirectiveLocation.FRAGMENT_SPREAD,
+            description="Location adjacent to a fragment spread.",
+        ),
+        "INLINE_FRAGMENT": GraphQLEnumValue(
+            DirectiveLocation.INLINE_FRAGMENT,
+            description="Location adjacent to an inline fragment.",
+        ),
+        "VARIABLE_DEFINITION": GraphQLEnumValue(
+            DirectiveLocation.VARIABLE_DEFINITION,
+            description="Location adjacent to a variable definition.",
+        ),
+        "SCHEMA": GraphQLEnumValue(
+            DirectiveLocation.SCHEMA,
+            description="Location adjacent to a schema definition.",
+        ),
+        "SCALAR": GraphQLEnumValue(
+            DirectiveLocation.SCALAR,
+            description="Location adjacent to a scalar definition.",
+        ),
+        "OBJECT": GraphQLEnumValue(
+            DirectiveLocation.OBJECT,
+            description="Location adjacent to an object type definition.",
+        ),
+        "FIELD_DEFINITION": GraphQLEnumValue(
+            DirectiveLocation.FIELD_DEFINITION,
+            description="Location adjacent to a field definition.",
+        ),
+        "ARGUMENT_DEFINITION": GraphQLEnumValue(
+            DirectiveLocation.ARGUMENT_DEFINITION,
+            description="Location adjacent to an argument definition.",
+        ),
+        "INTERFACE": GraphQLEnumValue(
+            DirectiveLocation.INTERFACE,
+            description="Location adjacent to an interface definition.",
+        ),
+        "UNION": GraphQLEnumValue(
+            DirectiveLocation.UNION,
+            description="Location adjacent to a union definition.",
+        ),
+        "ENUM": GraphQLEnumValue(
+            DirectiveLocation.ENUM,
+            description="Location adjacent to an enum definition.",
+        ),
+        "ENUM_VALUE": GraphQLEnumValue(
+            DirectiveLocation.ENUM_VALUE,
+            description="Location adjacent to an enum value definition.",
+        ),
+        "INPUT_OBJECT": GraphQLEnumValue(
+            DirectiveLocation.INPUT_OBJECT,
+            description="Location adjacent to an input object type definition.",
+        ),
+        "INPUT_FIELD_DEFINITION": GraphQLEnumValue(
+            DirectiveLocation.INPUT_FIELD_DEFINITION,
+            description="Location adjacent to an input object field definition.",
+        ),
+    },
+)
+
+
+__Type: GraphQLObjectType = GraphQLObjectType(
+    name="__Type",
+    description="The fundamental unit of any GraphQL Schema is the type."
+    " There are many kinds of types in GraphQL as represented"
+    " by the `__TypeKind` enum.\n\nDepending on the kind of a"
+    " type, certain fields describe information about that type."
+    " Scalar types provide no information beyond a name, description"
+    " and optional `specifiedByUrl`, while Enum types provide their values."
+    " Object and Interface types provide the fields they describe."
+    " Abstract types, Union and Interface, provide the Object"
+    " types possible at runtime. List and NonNull types compose"
+    " other types.",
+    fields=lambda: {
+        "kind": GraphQLField(
+            GraphQLNonNull(__TypeKind), resolve=TypeFieldResolvers.kind
+        ),
+        "name": GraphQLField(GraphQLString, resolve=TypeFieldResolvers.name),
+        "description": GraphQLField(
+            GraphQLString, resolve=TypeFieldResolvers.description
+        ),
+        "specifiedByUrl": GraphQLField(
+            GraphQLString, resolve=TypeFieldResolvers.specified_by_url
+        ),
+        "fields": GraphQLField(
+            GraphQLList(GraphQLNonNull(__Field)),
+            args={
+                "includeDeprecated": GraphQLArgument(
+                    GraphQLBoolean, default_value=False
+                )
+            },
+            resolve=TypeFieldResolvers.fields,
+        ),
+        "interfaces": GraphQLField(
+            GraphQLList(GraphQLNonNull(__Type)), resolve=TypeFieldResolvers.interfaces
+        ),
+        "possibleTypes": GraphQLField(
+            GraphQLList(GraphQLNonNull(__Type)),
+            resolve=TypeFieldResolvers.possible_types,
+        ),
+        "enumValues": GraphQLField(
+            GraphQLList(GraphQLNonNull(__EnumValue)),
+            args={
+                "includeDeprecated": GraphQLArgument(
+                    GraphQLBoolean, default_value=False
+                )
+            },
+            resolve=TypeFieldResolvers.enum_values,
+        ),
+        "inputFields": GraphQLField(
+            GraphQLList(GraphQLNonNull(__InputValue)),
+            resolve=TypeFieldResolvers.input_fields,
+        ),
+        "ofType": GraphQLField(__Type, resolve=TypeFieldResolvers.of_type),
+    },
+)
+
+
+class TypeFieldResolvers:
+    @staticmethod
+    def kind(type_, _info):
+        if is_scalar_type(type_):
+            return TypeKind.SCALAR
+        if is_object_type(type_):
+            return TypeKind.OBJECT
+        if is_interface_type(type_):
+            return TypeKind.INTERFACE
+        if is_union_type(type_):
+            return TypeKind.UNION
+        if is_enum_type(type_):
+            return TypeKind.ENUM
+        if is_input_object_type(type_):
+            return TypeKind.INPUT_OBJECT
+        if is_list_type(type_):
+            return TypeKind.LIST
+        if is_non_null_type(type_):
+            return TypeKind.NON_NULL
+
+        # Not reachable. All possible types have been considered.
+        raise TypeError(f"Unexpected type: {inspect(type_)}.")  # pragma: no cover
+
+    @staticmethod
+    def name(type_, _info):
+        return getattr(type_, "name", None)
+
+    @staticmethod
+    def description(type_, _info):
+        return getattr(type_, "description", None)
+
+    @staticmethod
+    def specified_by_url(type_, _info):
+        return getattr(type_, "specified_by_url", None)
+
+    # noinspection PyPep8Naming
+    @staticmethod
+    def fields(type_, _info, includeDeprecated=False):
+        if is_object_type(type_) or is_interface_type(type_):
+            items = type_.fields.items()
+            if not includeDeprecated:
+                return [item for item in items if not item[1].is_deprecated]
+            return list(items)
+
+    @staticmethod
+    def interfaces(type_, _info):
+        if is_object_type(type_) or is_interface_type(type_):
+            return type_.interfaces
+
+    @staticmethod
+    def possible_types(type_, info):
+        if is_abstract_type(type_):
+            return info.schema.get_possible_types(type_)
+
+    # noinspection PyPep8Naming
+    @staticmethod
+    def enum_values(type_, _info, includeDeprecated=False):
+        if is_enum_type(type_):
+            items = type_.values.items()
+            if not includeDeprecated:
+                return [item for item in items if not item[1].is_deprecated]
+            return items
+
+    @staticmethod
+    def input_fields(type_, _info):
+        if is_input_object_type(type_):
+            return type_.fields.items()
+
+    @staticmethod
+    def of_type(type_, _info):
+        return getattr(type_, "of_type", None)
+
+
+__Field: GraphQLObjectType = GraphQLObjectType(
+    name="__Field",
+    description="Object and Interface types are described by a list of Fields,"
+    " each of which has a name, potentially a list of arguments,"
+    " and a return type.",
+    fields=lambda: {
+        "name": GraphQLField(
+            GraphQLNonNull(GraphQLString), resolve=lambda item, _info: item[0]
+        ),
+        "description": GraphQLField(
+            GraphQLString, resolve=lambda item, _info: item[1].description
+        ),
+        "args": GraphQLField(
+            GraphQLNonNull(GraphQLList(GraphQLNonNull(__InputValue))),
+            resolve=lambda item, _info: item[1].args.items(),
+        ),
+        "type": GraphQLField(
+            GraphQLNonNull(__Type), resolve=lambda item, _info: item[1].type
+        ),
+        "isDeprecated": GraphQLField(
+            GraphQLNonNull(GraphQLBoolean),
+            resolve=lambda item, _info: item[1].is_deprecated,
+        ),
+        "deprecationReason": GraphQLField(
+            GraphQLString, resolve=lambda item, _info: item[1].deprecation_reason
+        ),
+    },
+)
+
+
+__InputValue: GraphQLObjectType = GraphQLObjectType(
+    name="__InputValue",
+    description="Arguments provided to Fields or Directives and the input"
+    " fields of an InputObject are represented as Input Values"
+    " which describe their type and optionally a default value.",
+    fields=lambda: {
+        "name": GraphQLField(
+            GraphQLNonNull(GraphQLString), resolve=InputValueFieldResolvers.name
+        ),
+        "description": GraphQLField(
+            GraphQLString, resolve=InputValueFieldResolvers.description
+        ),
+        "type": GraphQLField(
+            GraphQLNonNull(__Type), resolve=InputValueFieldResolvers.type
+        ),
+        "defaultValue": GraphQLField(
+            GraphQLString,
+            description="A GraphQL-formatted string representing"
+            " the default value for this input value.",
+            resolve=InputValueFieldResolvers.default_value,
+        ),
+    },
+)
+
+
+class InputValueFieldResolvers:
+    @staticmethod
+    def name(item, _info):
+        return item[0]
+
+    @staticmethod
+    def description(item, _info):
+        return item[1].description
+
+    @staticmethod
+    def type(item, _info):
+        return item[1].type
+
+    @staticmethod
+    def default_value(item, _info):
+        # Since ast_from_value needs graphql.type, it can only be imported later
+        from ..utilities import ast_from_value
+
+        value_ast = ast_from_value(item[1].default_value, item[1].type)
+        return print_ast(value_ast) if value_ast else None
+
+
+__EnumValue: GraphQLObjectType = GraphQLObjectType(
+    name="__EnumValue",
+    description="One possible value for a given Enum. Enum values are unique"
+    " values, not a placeholder for a string or numeric value."
+    " However an Enum value is returned in a JSON response as a"
+    " string.",
+    fields=lambda: {
+        "name": GraphQLField(
+            GraphQLNonNull(GraphQLString), resolve=lambda item, _info: item[0]
+        ),
+        "description": GraphQLField(
+            GraphQLString, resolve=lambda item, _info: item[1].description
+        ),
+        "isDeprecated": GraphQLField(
+            GraphQLNonNull(GraphQLBoolean),
+            resolve=lambda item, _info: item[1].is_deprecated,
+        ),
+        "deprecationReason": GraphQLField(
+            GraphQLString, resolve=lambda item, _info: item[1].deprecation_reason
+        ),
+    },
+)
+
+
+class TypeKind(Enum):
+    SCALAR = "scalar"
+    OBJECT = "object"
+    INTERFACE = "interface"
+    UNION = "union"
+    ENUM = "enum"
+    INPUT_OBJECT = "input object"
+    LIST = "list"
+    NON_NULL = "non-null"
+
+
+__TypeKind: GraphQLEnumType = GraphQLEnumType(
+    name="__TypeKind",
+    description="An enum describing what kind of type a given `__Type` is.",
+    values={
+        "SCALAR": GraphQLEnumValue(
+            TypeKind.SCALAR, description="Indicates this type is a scalar."
+        ),
+        "OBJECT": GraphQLEnumValue(
+            TypeKind.OBJECT,
+            description="Indicates this type is an object."
+            " `fields` and `interfaces` are valid fields.",
+        ),
+        "INTERFACE": GraphQLEnumValue(
+            TypeKind.INTERFACE,
+            description="Indicates this type is an interface."
+            " `fields`, `interfaces`, and `possibleTypes` are valid fields.",
+        ),
+        "UNION": GraphQLEnumValue(
+            TypeKind.UNION,
+            description="Indicates this type is a union."
+            " `possibleTypes` is a valid field.",
+        ),
+        "ENUM": GraphQLEnumValue(
+            TypeKind.ENUM,
+            description="Indicates this type is an enum."
+            " `enumValues` is a valid field.",
+        ),
+        "INPUT_OBJECT": GraphQLEnumValue(
+            TypeKind.INPUT_OBJECT,
+            description="Indicates this type is an input object."
+            " `inputFields` is a valid field.",
+        ),
+        "LIST": GraphQLEnumValue(
+            TypeKind.LIST,
+            description="Indicates this type is a list. `ofType` is a valid field.",
+        ),
+        "NON_NULL": GraphQLEnumValue(
+            TypeKind.NON_NULL,
+            description="Indicates this type is a non-null."
+            " `ofType` is a valid field.",
+        ),
+    },
+)
+
+
+SchemaMetaFieldDef = GraphQLField(
+    GraphQLNonNull(__Schema),  # name = '__schema'
+    description="Access the current type schema of this server.",
+    args={},
+    resolve=lambda _source, info: info.schema,
+)
+
+
+TypeMetaFieldDef = GraphQLField(
+    __Type,  # name = '__type'
+    description="Request the type information of a single type.",
+    args={"name": GraphQLArgument(GraphQLNonNull(GraphQLString))},
+    resolve=lambda _source, info, **args: info.schema.get_type(args["name"]),
+)
+
+
+TypeNameMetaFieldDef = GraphQLField(
+    GraphQLNonNull(GraphQLString),  # name='__typename'
+    description="The name of the current Object type at runtime.",
+    args={},
+    resolve=lambda _source, info, **_args: info.parent_type.name,
+)
+
+
+# Since double underscore names are subject to name mangling in Python,
+# the introspection classes are best imported via this dictionary:
+introspection_types = FrozenDict(
+    {
+        "__Schema": __Schema,
+        "__Directive": __Directive,
+        "__DirectiveLocation": __DirectiveLocation,
+        "__Type": __Type,
+        "__Field": __Field,
+        "__InputValue": __InputValue,
+        "__EnumValue": __EnumValue,
+        "__TypeKind": __TypeKind,
+    }
+)
+introspection_types.__doc__ = """A dictionary containing all introspection types."""
+
+
+def is_introspection_type(type_: GraphQLNamedType) -> bool:
+    """Check whether the given named GraphQL type is an introspection type."""
+    return type_.name in introspection_types
diff --git a/src/graphql/type/scalars.py b/src/graphql/type/scalars.py
new file mode 100644
index 0000000..fc3c13d
--- /dev/null
+++ b/src/graphql/type/scalars.py
@@ -0,0 +1,294 @@
+from math import isfinite
+from typing import Any
+
+from ..error import GraphQLError
+from ..pyutils import inspect, is_finite, is_integer, FrozenDict
+from ..language.ast import (
+    BooleanValueNode,
+    FloatValueNode,
+    IntValueNode,
+    StringValueNode,
+    ValueNode,
+)
+from ..language.printer import print_ast
+from .definition import GraphQLNamedType, GraphQLScalarType
+
+__all__ = [
+    "is_specified_scalar_type",
+    "specified_scalar_types",
+    "GraphQLInt",
+    "GraphQLFloat",
+    "GraphQLString",
+    "GraphQLBoolean",
+    "GraphQLID",
+]
+
+
+# As per the GraphQL Spec, Integers are only treated as valid when a valid
+# 32-bit signed integer, providing the broadest support across platforms.
+#
+# n.b. JavaScript's integers are safe between -(2^53 - 1) and 2^53 - 1 because
+# they are internally represented as IEEE 754 doubles,
+# while Python's integers may be arbitrarily large.
+MAX_INT = 2_147_483_647
+MIN_INT = -2_147_483_648
+
+
+def serialize_int(output_value: Any) -> int:
+    if isinstance(output_value, bool):
+        return 1 if output_value else 0
+    try:
+        if isinstance(output_value, int):
+            num = output_value
+        elif isinstance(output_value, float):
+            num = int(output_value)
+            if num != output_value:
+                raise ValueError
+        elif not output_value and isinstance(output_value, str):
+            output_value = ""
+            raise ValueError
+        else:
+            num = int(output_value)  # raises ValueError if not an integer
+    except (OverflowError, ValueError, TypeError):
+        raise GraphQLError(
+            "Int cannot represent non-integer value: " + inspect(output_value)
+        )
+    if not MIN_INT <= num <= MAX_INT:
+        raise GraphQLError(
+            "Int cannot represent non 32-bit signed integer value: "
+            + inspect(output_value)
+        )
+    return num
+
+
+def coerce_int(input_value: Any) -> int:
+    if not is_integer(input_value):
+        raise GraphQLError(
+            "Int cannot represent non-integer value: " + inspect(input_value)
+        )
+    if not MIN_INT <= input_value <= MAX_INT:
+        raise GraphQLError(
+            "Int cannot represent non 32-bit signed integer value: "
+            + inspect(input_value)
+        )
+    return int(input_value)
+
+
+def parse_int_literal(value_node: ValueNode, _variables: Any = None) -> int:
+    """Parse an integer value node in the AST."""
+    if not isinstance(value_node, IntValueNode):
+        raise GraphQLError(
+            "Int cannot represent non-integer value: " + print_ast(value_node),
+            value_node,
+        )
+    num = int(value_node.value)
+    if not MIN_INT <= num <= MAX_INT:
+        raise GraphQLError(
+            "Int cannot represent non 32-bit signed integer value: "
+            + print_ast(value_node),
+            value_node,
+        )
+    return num
+
+
+GraphQLInt = GraphQLScalarType(
+    name="Int",
+    description="The `Int` scalar type represents"
+    " non-fractional signed whole numeric values."
+    " Int can represent values between -(2^31) and 2^31 - 1.",
+    serialize=serialize_int,
+    parse_value=coerce_int,
+    parse_literal=parse_int_literal,
+)
+
+
+def serialize_float(output_value: Any) -> float:
+    if isinstance(output_value, bool):
+        return 1 if output_value else 0
+    try:
+        if not output_value and isinstance(output_value, str):
+            output_value = ""
+            raise ValueError
+        num = output_value if isinstance(output_value, float) else float(output_value)
+        if not isfinite(num):
+            raise ValueError
+    except (ValueError, TypeError):
+        raise GraphQLError(
+            "Float cannot represent non numeric value: " + inspect(output_value)
+        )
+    return num
+
+
+def coerce_float(input_value: Any) -> float:
+    if not is_finite(input_value):
+        raise GraphQLError(
+            "Float cannot represent non numeric value: " + inspect(input_value)
+        )
+    return float(input_value)
+
+
+def parse_float_literal(value_node: ValueNode, _variables: Any = None) -> float:
+    """Parse a float value node in the AST."""
+    if not isinstance(value_node, (FloatValueNode, IntValueNode)):
+        raise GraphQLError(
+            "Float cannot represent non numeric value: " + print_ast(value_node),
+            value_node,
+        )
+    return float(value_node.value)
+
+
+GraphQLFloat = GraphQLScalarType(
+    name="Float",
+    description="The `Float` scalar type represents"
+    " signed double-precision fractional values"
+    " as specified by [IEEE 754]"
+    "(https://en.wikipedia.org/wiki/IEEE_floating_point).",
+    serialize=serialize_float,
+    parse_value=coerce_float,
+    parse_literal=parse_float_literal,
+)
+
+
+def serialize_string(output_value: Any) -> str:
+    if isinstance(output_value, str):
+        return output_value
+    if isinstance(output_value, bool):
+        return "true" if output_value else "false"
+    if is_finite(output_value):
+        return str(output_value)
+    # do not serialize builtin types as strings, but allow serialization of custom
+    # types via their `__str__` method
+    if type(output_value).__module__ == "builtins":
+        raise GraphQLError("String cannot represent value: " + inspect(output_value))
+    return str(output_value)
+
+
+def coerce_string(input_value: Any) -> str:
+    if not isinstance(input_value, str):
+        raise GraphQLError(
+            "String cannot represent a non string value: " + inspect(input_value)
+        )
+    return input_value
+
+
+def parse_string_literal(value_node: ValueNode, _variables: Any = None) -> str:
+    """Parse a string value node in the AST."""
+    if not isinstance(value_node, StringValueNode):
+        raise GraphQLError(
+            "String cannot represent a non string value: " + print_ast(value_node),
+            value_node,
+        )
+    return value_node.value
+
+
+GraphQLString = GraphQLScalarType(
+    name="String",
+    description="The `String` scalar type represents textual data,"
+    " represented as UTF-8 character sequences."
+    " The String type is most often used by GraphQL"
+    " to represent free-form human-readable text.",
+    serialize=serialize_string,
+    parse_value=coerce_string,
+    parse_literal=parse_string_literal,
+)
+
+
+def serialize_boolean(output_value: Any) -> bool:
+    if isinstance(output_value, bool):
+        return output_value
+    if is_finite(output_value):
+        return bool(output_value)
+    raise GraphQLError(
+        "Boolean cannot represent a non boolean value: " + inspect(output_value)
+    )
+
+
+def coerce_boolean(input_value: Any) -> bool:
+    if not isinstance(input_value, bool):
+        raise GraphQLError(
+            "Boolean cannot represent a non boolean value: " + inspect(input_value)
+        )
+    return input_value
+
+
+def parse_boolean_literal(value_node: ValueNode, _variables: Any = None) -> bool:
+    """Parse a boolean value node in the AST."""
+    if not isinstance(value_node, BooleanValueNode):
+        raise GraphQLError(
+            "Boolean cannot represent a non boolean value: " + print_ast(value_node),
+            value_node,
+        )
+    return value_node.value
+
+
+GraphQLBoolean = GraphQLScalarType(
+    name="Boolean",
+    description="The `Boolean` scalar type represents `true` or `false`.",
+    serialize=serialize_boolean,
+    parse_value=coerce_boolean,
+    parse_literal=parse_boolean_literal,
+)
+
+
+def serialize_id(output_value: Any) -> str:
+    if isinstance(output_value, str):
+        return output_value
+    if is_integer(output_value):
+        return str(int(output_value))
+    # do not serialize builtin types as IDs, but allow serialization of custom types
+    # via their `__str__` method
+    if type(output_value).__module__ == "builtins":
+        raise GraphQLError("ID cannot represent value: " + inspect(output_value))
+    return str(output_value)
+
+
+def coerce_id(input_value: Any) -> str:
+    if isinstance(input_value, str):
+        return input_value
+    if is_integer(input_value):
+        return str(input_value)
+    raise GraphQLError("ID cannot represent value: " + inspect(input_value))
+
+
+def parse_id_literal(value_node: ValueNode, _variables: Any = None) -> str:
+    """Parse an ID value node in the AST."""
+    if not isinstance(value_node, (StringValueNode, IntValueNode)):
+        raise GraphQLError(
+            "ID cannot represent a non-string and non-integer value: "
+            + print_ast(value_node),
+            value_node,
+        )
+    return value_node.value
+
+
+GraphQLID = GraphQLScalarType(
+    name="ID",
+    description="The `ID` scalar type represents a unique identifier,"
+    " often used to refetch an object or as key for a cache."
+    " The ID type appears in a JSON response as a String; however,"
+    " it is not intended to be human-readable. When expected as an"
+    ' input type, any string (such as `"4"`) or integer (such as'
+    " `4`) input value will be accepted as an ID.",
+    serialize=serialize_id,
+    parse_value=coerce_id,
+    parse_literal=parse_id_literal,
+)
+
+
+specified_scalar_types: FrozenDict[str, GraphQLScalarType] = FrozenDict(
+    {
+        type_.name: type_
+        for type_ in (
+            GraphQLString,
+            GraphQLInt,
+            GraphQLFloat,
+            GraphQLBoolean,
+            GraphQLID,
+        )
+    }
+)
+
+
+def is_specified_scalar_type(type_: GraphQLNamedType) -> bool:
+    """Check whether the given named GraphQL type is a specified scalar type."""
+    return type_.name in specified_scalar_types
diff --git a/src/graphql/type/schema.py b/src/graphql/type/schema.py
new file mode 100644
index 0000000..06f73bf
--- /dev/null
+++ b/src/graphql/type/schema.py
@@ -0,0 +1,398 @@
+from typing import (
+    Any,
+    Collection,
+    Dict,
+    List,
+    NamedTuple,
+    Optional,
+    Set,
+    Union,
+    cast,
+)
+
+from ..error import GraphQLError
+from ..language import ast
+from ..pyutils import inspect, is_collection, is_description, FrozenList
+from .definition import (
+    GraphQLAbstractType,
+    GraphQLInterfaceType,
+    GraphQLInputObjectType,
+    GraphQLNamedType,
+    GraphQLObjectType,
+    GraphQLUnionType,
+    GraphQLType,
+    get_named_type,
+    is_input_object_type,
+    is_interface_type,
+    is_object_type,
+    is_union_type,
+)
+from .directives import GraphQLDirective, specified_directives, is_directive
+from .introspection import introspection_types
+
+__all__ = ["GraphQLSchema", "is_schema", "assert_schema"]
+
+
+TypeMap = Dict[str, GraphQLNamedType]
+
+
+class InterfaceImplementations(NamedTuple):
+
+    objects: List[GraphQLObjectType]
+    interfaces: List[GraphQLInterfaceType]
+
+
+class GraphQLSchema:
+    """Schema Definition
+
+    A Schema is created by supplying the root types of each type of operation, query
+    and mutation (optional). A schema definition is then supplied to the validator
+    and executor.
+
+    Example::
+
+        MyAppSchema = GraphQLSchema(
+          query=MyAppQueryRootType,
+          mutation=MyAppMutationRootType)
+
+    Note: When the schema is constructed, by default only the types that are
+    reachable by traversing the root types are included, other types must be
+    explicitly referenced.
+
+    Example::
+
+        character_interface = GraphQLInterfaceType('Character', ...)
+
+        human_type = GraphQLObjectType(
+            'Human', interfaces=[character_interface], ...)
+
+        droid_type = GraphQLObjectType(
+            'Droid', interfaces: [character_interface], ...)
+
+        schema = GraphQLSchema(
+            query=GraphQLObjectType('Query',
+                fields={'hero': GraphQLField(character_interface, ....)}),
+            ...
+            # Since this schema references only the `Character` interface it's
+            # necessary to explicitly list the types that implement it if
+            # you want them to be included in the final schema.
+            types=[human_type, droid_type])
+
+    Note: If a list of ``directives`` is provided to GraphQLSchema, that will be the
+    exact list of directives represented and allowed. If ``directives`` is not provided,
+    then a default set of the specified directives (e.g. @include and @skip) will be
+    used. If you wish to provide *additional* directives to these specified directives,
+    you must explicitly declare them. Example::
+
+        MyAppSchema = GraphQLSchema(
+          ...
+          directives=specified_directives + [my_custom_directive])
+    """
+
+    query_type: Optional[GraphQLObjectType]
+    mutation_type: Optional[GraphQLObjectType]
+    subscription_type: Optional[GraphQLObjectType]
+    type_map: TypeMap
+    directives: FrozenList[GraphQLDirective]
+    description: Optional[str]
+    extensions: Optional[Dict[str, Any]]
+    ast_node: Optional[ast.SchemaDefinitionNode]
+    extension_ast_nodes: Optional[FrozenList[ast.SchemaExtensionNode]]
+
+    _implementations_map: Dict[str, InterfaceImplementations]
+    _sub_type_map: Dict[str, Set[str]]
+    _validation_errors: Optional[List[GraphQLError]]
+
+    def __init__(
+        self,
+        query: Optional[GraphQLObjectType] = None,
+        mutation: Optional[GraphQLObjectType] = None,
+        subscription: Optional[GraphQLObjectType] = None,
+        types: Optional[Collection[GraphQLNamedType]] = None,
+        directives: Optional[Collection[GraphQLDirective]] = None,
+        description: Optional[str] = None,
+        extensions: Optional[Dict[str, Any]] = None,
+        ast_node: Optional[ast.SchemaDefinitionNode] = None,
+        extension_ast_nodes: Optional[Collection[ast.SchemaExtensionNode]] = None,
+        assume_valid: bool = False,
+    ) -> None:
+        """Initialize GraphQL schema.
+
+        If this schema was built from a source known to be valid, then it may be marked
+        with ``assume_valid`` to avoid an additional type system validation.
+        """
+        self._validation_errors = [] if assume_valid else None
+
+        # Check for common mistakes during construction to produce clear and early
+        # error messages, but we leave the specific tests for the validation.
+        if query and not isinstance(query, GraphQLType):
+            raise TypeError("Expected query to be a GraphQL type.")
+        if mutation and not isinstance(mutation, GraphQLType):
+            raise TypeError("Expected mutation to be a GraphQL type.")
+        if subscription and not isinstance(subscription, GraphQLType):
+            raise TypeError("Expected subscription to be a GraphQL type.")
+        if types is None:
+            types = []
+        else:
+            if not is_collection(types) or not all(
+                isinstance(type_, GraphQLType) for type_ in types
+            ):
+                raise TypeError(
+                    "Schema types must be specified as a collection of GraphQL types."
+                )
+        if directives is not None:
+            # noinspection PyUnresolvedReferences
+            if not is_collection(directives):
+                raise TypeError("Schema directives must be a collection.")
+            if not isinstance(directives, FrozenList):
+                directives = FrozenList(directives)
+        if description is not None and not is_description(description):
+            raise TypeError("Schema description must be a string.")
+        if extensions is not None and (
+            not isinstance(extensions, dict)
+            or not all(isinstance(key, str) for key in extensions)
+        ):
+            raise TypeError("Schema extensions must be a dictionary with string keys.")
+        if ast_node and not isinstance(ast_node, ast.SchemaDefinitionNode):
+            raise TypeError("Schema AST node must be a SchemaDefinitionNode.")
+        if extension_ast_nodes:
+            if not is_collection(extension_ast_nodes) or not all(
+                isinstance(node, ast.SchemaExtensionNode)
+                for node in extension_ast_nodes
+            ):
+                raise TypeError(
+                    "Schema extension AST nodes must be specified"
+                    " as a collection of SchemaExtensionNode instances."
+                )
+            if not isinstance(extension_ast_nodes, FrozenList):
+                extension_ast_nodes = FrozenList(extension_ast_nodes)
+
+        self.description = description
+        self.extensions = extensions
+        self.ast_node = ast_node
+        self.extension_ast_nodes = (
+            cast(FrozenList[ast.SchemaExtensionNode], extension_ast_nodes)
+            if extension_ast_nodes
+            else None
+        )
+
+        self.query_type = query
+        self.mutation_type = mutation
+        self.subscription_type = subscription
+        # Provide specified directives (e.g. @include and @skip) by default
+        self.directives = (
+            specified_directives
+            if directives is None
+            else cast(FrozenList[GraphQLDirective], directives)
+        )
+
+        # To preserve order of user-provided types, we add first to add them to
+        # the set of "collected" types, so `collect_referenced_types` ignore them.
+        if types:
+            all_referenced_types = TypeSet.with_initial_types(types)
+            collect_referenced_types = all_referenced_types.collect_referenced_types
+            for type_ in types:
+                # When we are ready to process this type, we remove it from "collected"
+                # types and then add it together with all dependent types in the correct
+                # position.
+                del all_referenced_types[type_]
+                collect_referenced_types(type_)
+        else:
+            all_referenced_types = TypeSet()
+            collect_referenced_types = all_referenced_types.collect_referenced_types
+
+        if query:
+            collect_referenced_types(query)
+        if mutation:
+            collect_referenced_types(mutation)
+        if subscription:
+            collect_referenced_types(subscription)
+
+        for directive in self.directives:
+            # Directives are not validated until validate_schema() is called.
+            if is_directive(directive):
+                for arg in directive.args.values():
+                    collect_referenced_types(arg.type)
+        collect_referenced_types(introspection_types["__Schema"])
+
+        # Storing the resulting map for reference by the schema.
+        type_map: TypeMap = {}
+        self.type_map = type_map
+
+        self._sub_type_map = {}
+
+        # Keep track of all implementations by interface name.
+        implementations_map: Dict[str, InterfaceImplementations] = {}
+        self._implementations_map = implementations_map
+
+        for named_type in all_referenced_types:
+            if not named_type:
+                continue
+
+            type_name = getattr(named_type, "name", None)
+            if not type_name:
+                raise TypeError(
+                    "One of the provided types for building the Schema"
+                    " is missing a name.",
+                )
+            if type_name in type_map:
+                raise TypeError(
+                    "Schema must contain uniquely named types"
+                    f" but contains multiple types named '{type_name}'."
+                )
+            type_map[type_name] = named_type
+
+            if is_interface_type(named_type):
+                named_type = cast(GraphQLInterfaceType, named_type)
+                # Store implementations by interface.
+                for iface in named_type.interfaces:
+                    if is_interface_type(iface):
+                        iface = cast(GraphQLInterfaceType, iface)
+                        if iface.name in implementations_map:
+                            implementations = implementations_map[iface.name]
+                        else:
+                            implementations = implementations_map[
+                                iface.name
+                            ] = InterfaceImplementations(objects=[], interfaces=[])
+
+                        implementations.interfaces.append(named_type)
+            elif is_object_type(named_type):
+                named_type = cast(GraphQLObjectType, named_type)
+                # Store implementations by objects.
+                for iface in named_type.interfaces:
+                    if is_interface_type(iface):
+                        iface = cast(GraphQLInterfaceType, iface)
+                        if iface.name in implementations_map:
+                            implementations = implementations_map[iface.name]
+                        else:
+                            implementations = implementations_map[
+                                iface.name
+                            ] = InterfaceImplementations(objects=[], interfaces=[])
+
+                        implementations.objects.append(named_type)
+
+    def to_kwargs(self) -> Dict[str, Any]:
+        return dict(
+            query=self.query_type,
+            mutation=self.mutation_type,
+            subscription=self.subscription_type,
+            types=FrozenList(self.type_map.values()) or None,
+            directives=self.directives[:],
+            description=self.description,
+            extensions=self.extensions,
+            ast_node=self.ast_node,
+            extension_ast_nodes=self.extension_ast_nodes or FrozenList(),
+            assume_valid=self._validation_errors is not None,
+        )
+
+    def get_type(self, name: str) -> Optional[GraphQLNamedType]:
+        return self.type_map.get(name)
+
+    def get_possible_types(
+        self, abstract_type: GraphQLAbstractType
+    ) -> List[GraphQLObjectType]:
+        """Get list of all possible concrete types for given abstract type."""
+        return (
+            cast(GraphQLUnionType, abstract_type).types
+            if is_union_type(abstract_type)
+            else self.get_implementations(
+                cast(GraphQLInterfaceType, abstract_type)
+            ).objects
+        )
+
+    def get_implementations(
+        self, interface_type: GraphQLInterfaceType
+    ) -> InterfaceImplementations:
+        return self._implementations_map.get(
+            interface_type.name, InterfaceImplementations(objects=[], interfaces=[])
+        )
+
+    def is_possible_type(
+        self, abstract_type: GraphQLAbstractType, possible_type: GraphQLObjectType
+    ) -> bool:
+        """Check whether a concrete type is possible for an abstract type.
+
+        Deprecated: Use is_sub_type() instead.
+        """
+        return self.is_sub_type(abstract_type, possible_type)
+
+    def is_sub_type(
+        self, abstract_type: GraphQLAbstractType, maybe_sub_type: GraphQLNamedType,
+    ) -> bool:
+        """Check whether a type is a subtype of a given abstract type."""
+        types = self._sub_type_map.get(abstract_type.name)
+        if types is None:
+            types = set()
+            add = types.add
+            if is_union_type(abstract_type):
+                for type_ in cast(GraphQLUnionType, abstract_type).types:
+                    add(type_.name)
+            else:
+                implementations = self.get_implementations(
+                    cast(GraphQLInterfaceType, abstract_type)
+                )
+                for type_ in implementations.objects:
+                    add(type_.name)
+                for type_ in implementations.interfaces:
+                    add(type_.name)
+            self._sub_type_map[abstract_type.name] = types
+        return maybe_sub_type.name in types
+
+    def get_directive(self, name: str) -> Optional[GraphQLDirective]:
+        for directive in self.directives:
+            if directive.name == name:
+                return directive
+        return None
+
+    @property
+    def validation_errors(self) -> Optional[List[GraphQLError]]:
+        return self._validation_errors
+
+
+class TypeSet(Dict[GraphQLNamedType, None]):
+    """An ordered set of types that can be collected starting from initial types."""
+
+    @classmethod
+    def with_initial_types(cls, types: Collection[GraphQLType]) -> "TypeSet":
+        return cast(TypeSet, super().fromkeys(types))
+
+    def collect_referenced_types(self, type_: GraphQLType) -> None:
+        """Recursive function supplementing the type starting from an initial type."""
+        named_type = get_named_type(type_)
+
+        if named_type in self:
+            return
+
+        self[named_type] = None
+
+        collect_referenced_types = self.collect_referenced_types
+        if is_union_type(named_type):
+            named_type = cast(GraphQLUnionType, named_type)
+            for member_type in named_type.types:
+                collect_referenced_types(member_type)
+        elif is_object_type(named_type) or is_interface_type(named_type):
+            named_type = cast(
+                Union[GraphQLObjectType, GraphQLInterfaceType], named_type
+            )
+            for interface_type in named_type.interfaces:
+                collect_referenced_types(interface_type)
+
+            for field in named_type.fields.values():
+                collect_referenced_types(field.type)
+                for arg in field.args.values():
+                    collect_referenced_types(arg.type)
+        elif is_input_object_type(named_type):
+            named_type = cast(GraphQLInputObjectType, named_type)
+            for field in named_type.fields.values():
+                collect_referenced_types(field.type)
+
+
+def is_schema(schema: Any) -> bool:
+    """Test if the given value is a GraphQL schema."""
+    return isinstance(schema, GraphQLSchema)
+
+
+def assert_schema(schema: Any) -> GraphQLSchema:
+    if not is_schema(schema):
+        raise TypeError(f"Expected {inspect(schema)} to be a GraphQL schema.")
+    return cast(GraphQLSchema, schema)
diff --git a/src/graphql/type/validate.py b/src/graphql/type/validate.py
new file mode 100644
index 0000000..fdd0c18
--- /dev/null
+++ b/src/graphql/type/validate.py
@@ -0,0 +1,578 @@
+from operator import attrgetter, itemgetter
+from typing import (
+    Any,
+    Callable,
+    Collection,
+    Dict,
+    List,
+    Optional,
+    Set,
+    Tuple,
+    Union,
+    cast,
+)
+
+from ..error import GraphQLError, located_error
+from ..pyutils import inspect
+from ..language import NamedTypeNode, Node, OperationType, OperationTypeDefinitionNode
+from .definition import (
+    GraphQLEnumType,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInterfaceType,
+    GraphQLObjectType,
+    GraphQLUnionType,
+    is_enum_type,
+    is_input_object_type,
+    is_input_type,
+    is_interface_type,
+    is_named_type,
+    is_non_null_type,
+    is_object_type,
+    is_output_type,
+    is_union_type,
+    is_required_argument,
+)
+from ..utilities.assert_valid_name import is_valid_name_error
+from ..utilities.type_comparators import is_equal_type, is_type_sub_type_of
+from .directives import is_directive, GraphQLDirective
+from .introspection import is_introspection_type
+from .schema import GraphQLSchema, assert_schema
+
+__all__ = ["validate_schema", "assert_valid_schema"]
+
+
+def validate_schema(schema: GraphQLSchema) -> List[GraphQLError]:
+    """Validate a GraphQL schema.
+
+    Implements the "Type Validation" sub-sections of the specification's "Type System"
+    section.
+
+    Validation runs synchronously, returning a list of encountered errors, or an empty
+    list if no errors were encountered and the Schema is valid.
+    """
+    # First check to ensure the provided value is in fact a GraphQLSchema.
+    assert_schema(schema)
+
+    # If this Schema has already been validated, return the previous results.
+    # noinspection PyProtectedMember
+    errors = schema._validation_errors
+    if errors is None:
+
+        # Validate the schema, producing a list of errors.
+        context = SchemaValidationContext(schema)
+        context.validate_root_types()
+        context.validate_directives()
+        context.validate_types()
+
+        # Persist the results of validation before returning to ensure validation does
+        # not run multiple times for this schema.
+        errors = context.errors
+        schema._validation_errors = errors
+
+    return errors
+
+
+def assert_valid_schema(schema: GraphQLSchema) -> None:
+    """Utility function which asserts a schema is valid.
+
+    Throws a TypeError if the schema is invalid.
+    """
+    errors = validate_schema(schema)
+    if errors:
+        raise TypeError("\n\n".join(error.message for error in errors))
+
+
+class SchemaValidationContext:
+    """Utility class providing a context for schema validation."""
+
+    errors: List[GraphQLError]
+    schema: GraphQLSchema
+
+    def __init__(self, schema: GraphQLSchema):
+        self.errors = []
+        self.schema = schema
+
+    def report_error(
+        self,
+        message: str,
+        nodes: Union[Optional[Node], Collection[Optional[Node]]] = None,
+    ) -> None:
+        if nodes and not isinstance(nodes, Node):
+            nodes = [node for node in nodes if node]
+        nodes = cast(Optional[Collection[Node]], nodes)
+        self.add_error(GraphQLError(message, nodes))
+
+    def add_error(self, error: GraphQLError) -> None:
+        self.errors.append(error)
+
+    def validate_root_types(self) -> None:
+        schema = self.schema
+
+        query_type = schema.query_type
+        if not query_type:
+            self.report_error("Query root type must be provided.", schema.ast_node)
+        elif not is_object_type(query_type):
+            self.report_error(
+                f"Query root type must be Object type, it cannot be {query_type}.",
+                get_operation_type_node(schema, OperationType.QUERY)
+                or query_type.ast_node,
+            )
+
+        mutation_type = schema.mutation_type
+        if mutation_type and not is_object_type(mutation_type):
+            self.report_error(
+                "Mutation root type must be Object type if provided,"
+                f" it cannot be {mutation_type}.",
+                get_operation_type_node(schema, OperationType.MUTATION)
+                or mutation_type.ast_node,
+            )
+
+        subscription_type = schema.subscription_type
+        if subscription_type and not is_object_type(subscription_type):
+            self.report_error(
+                "Subscription root type must be Object type if provided,"
+                f" it cannot be {subscription_type}.",
+                get_operation_type_node(schema, OperationType.SUBSCRIPTION)
+                or subscription_type.ast_node,
+            )
+
+    def validate_directives(self) -> None:
+        directives = self.schema.directives
+        for directive in directives:
+            # Ensure all directives are in fact GraphQL directives.
+            if not is_directive(directive):
+                self.report_error(
+                    f"Expected directive but got: {inspect(directive)}.",
+                    getattr(directive, "ast_node", None),
+                )
+                continue
+
+            # Ensure they are named correctly.
+            self.validate_name(directive)
+
+            # Ensure the arguments are valid.
+            for arg_name, arg in directive.args.items():
+                # Ensure they are named correctly.
+                self.validate_name(arg, arg_name)
+
+                # Ensure the type is an input type.
+                if not is_input_type(arg.type):
+                    self.report_error(
+                        f"The type of @{directive.name}({arg_name}:)"
+                        f" must be Input Type but got: {inspect(arg.type)}.",
+                        arg.ast_node,
+                    )
+
+    def validate_name(self, node: Any, name: Optional[str] = None) -> None:
+        # Ensure names are valid, however introspection types opt out.
+        try:
+            if not name:
+                name = node.name
+            name = cast(str, name)
+            ast_node = node.ast_node
+        except AttributeError:  # pragma: no cover
+            pass
+        else:
+            error = is_valid_name_error(name)
+            if error:
+                self.add_error(located_error(error, ast_node))
+
+    def validate_types(self) -> None:
+        validate_input_object_circular_refs = InputObjectCircularRefsValidator(self)
+        for type_ in self.schema.type_map.values():
+
+            # Ensure all provided types are in fact GraphQL type.
+            if not is_named_type(type_):
+                self.report_error(
+                    f"Expected GraphQL named type but got: {inspect(type_)}.",
+                    type_.ast_node if is_named_type(type_) else None,
+                )
+                continue
+
+            # Ensure it is named correctly (excluding introspection types).
+            if not is_introspection_type(type_):
+                self.validate_name(type_)
+
+            if is_object_type(type_):
+                type_ = cast(GraphQLObjectType, type_)
+                # Ensure fields are valid
+                self.validate_fields(type_)
+
+                # Ensure objects implement the interfaces they claim to.
+                self.validate_interfaces(type_)
+            elif is_interface_type(type_):
+                type_ = cast(GraphQLInterfaceType, type_)
+                # Ensure fields are valid.
+                self.validate_fields(type_)
+
+                # Ensure interfaces implement the interfaces they claim to.
+                self.validate_interfaces(type_)
+            elif is_union_type(type_):
+                type_ = cast(GraphQLUnionType, type_)
+                # Ensure Unions include valid member types.
+                self.validate_union_members(type_)
+            elif is_enum_type(type_):
+                type_ = cast(GraphQLEnumType, type_)
+                # Ensure Enums have valid values.
+                self.validate_enum_values(type_)
+            elif is_input_object_type(type_):
+                type_ = cast(GraphQLInputObjectType, type_)
+                # Ensure Input Object fields are valid.
+                self.validate_input_fields(type_)
+
+                # Ensure Input Objects do not contain non-nullable circular references
+                validate_input_object_circular_refs(type_)
+
+    def validate_fields(
+        self, type_: Union[GraphQLObjectType, GraphQLInterfaceType]
+    ) -> None:
+        fields = type_.fields
+
+        # Objects and Interfaces both must define one or more fields.
+        if not fields:
+            self.report_error(
+                f"Type {type_.name} must define one or more fields.",
+                get_all_nodes(type_),
+            )
+
+        for field_name, field in fields.items():
+
+            # Ensure they are named correctly.
+            self.validate_name(field, field_name)
+
+            # Ensure the type is an output type
+            if not is_output_type(field.type):
+                self.report_error(
+                    f"The type of {type_.name}.{field_name}"
+                    f" must be Output Type but got: {inspect(field.type)}.",
+                    field.ast_node and field.ast_node.type,
+                )
+
+            # Ensure the arguments are valid.
+            for arg_name, arg in field.args.items():
+                # Ensure they are named correctly.
+                self.validate_name(arg, arg_name)
+
+                # Ensure the type is an input type.
+                if not is_input_type(arg.type):
+                    self.report_error(
+                        f"The type of {type_.name}.{field_name}({arg_name}:)"
+                        f" must be Input Type but got: {inspect(arg.type)}.",
+                        arg.ast_node and arg.ast_node.type,
+                    )
+
+    def validate_interfaces(
+        self, type_: Union[GraphQLObjectType, GraphQLInterfaceType]
+    ) -> None:
+        iface_type_names: Set[str] = set()
+        for iface in type_.interfaces:
+            if not is_interface_type(iface):
+                self.report_error(
+                    f"Type {type_.name} must only implement Interface"
+                    f" types, it cannot implement {inspect(iface)}.",
+                    get_all_implements_interface_nodes(type_, iface),
+                )
+                continue
+
+            if type_ is iface:
+                self.report_error(
+                    f"Type {type_.name} cannot implement itself"
+                    " because it would create a circular reference.",
+                    get_all_implements_interface_nodes(type_, iface),
+                )
+
+            if iface.name in iface_type_names:
+                self.report_error(
+                    f"Type {type_.name} can only implement {iface.name} once.",
+                    get_all_implements_interface_nodes(type_, iface),
+                )
+                continue
+
+            iface_type_names.add(iface.name)
+
+            self.validate_type_implements_ancestors(type_, iface)
+            self.validate_type_implements_interface(type_, iface)
+
+    def validate_type_implements_interface(
+        self,
+        type_: Union[GraphQLObjectType, GraphQLInterfaceType],
+        iface: GraphQLInterfaceType,
+    ) -> None:
+        type_fields, iface_fields = type_.fields, iface.fields
+
+        # Assert each interface field is implemented.
+        for field_name, iface_field in iface_fields.items():
+            type_field = type_fields.get(field_name)
+
+            # Assert interface field exists on object.
+            if not type_field:
+                self.report_error(
+                    f"Interface field {iface.name}.{field_name}"
+                    f" expected but {type_.name} does not provide it.",
+                    [iface_field.ast_node, *get_all_nodes(type_)],
+                )
+                continue
+
+            # Assert interface field type is satisfied by type field type, by being
+            # a valid subtype (covariant).
+            if not is_type_sub_type_of(self.schema, type_field.type, iface_field.type):
+                self.report_error(
+                    f"Interface field {iface.name}.{field_name}"
+                    f" expects type {iface_field.type}"
+                    f" but {type_.name}.{field_name}"
+                    f" is type {type_field.type}.",
+                    [
+                        iface_field.ast_node and iface_field.ast_node.type,
+                        type_field.ast_node and type_field.ast_node.type,
+                    ],
+                )
+
+            # Assert each interface field arg is implemented.
+            for arg_name, iface_arg in iface_field.args.items():
+                type_arg = type_field.args.get(arg_name)
+
+                # Assert interface field arg exists on object field.
+                if not type_arg:
+                    self.report_error(
+                        "Interface field argument"
+                        f" {iface.name}.{field_name}({arg_name}:)"
+                        f" expected but {type_.name}.{field_name}"
+                        " does not provide it.",
+                        [iface_arg.ast_node, type_field.ast_node],
+                    )
+                    continue
+
+                # Assert interface field arg type matches object field arg type
+                # (invariant).
+                if not is_equal_type(iface_arg.type, type_arg.type):
+                    self.report_error(
+                        "Interface field argument"
+                        f" {iface.name}.{field_name}({arg_name}:)"
+                        f" expects type {iface_arg.type}"
+                        f" but {type_.name}.{field_name}({arg_name}:)"
+                        f" is type {type_arg.type}.",
+                        [
+                            iface_arg.ast_node and iface_arg.ast_node.type,
+                            type_arg.ast_node and type_arg.ast_node.type,
+                        ],
+                    )
+
+            # Assert additional arguments must not be required.
+            for arg_name, type_arg in type_field.args.items():
+                iface_arg = iface_field.args.get(arg_name)
+                if not iface_arg and is_required_argument(type_arg):
+                    self.report_error(
+                        f"Object field {type_.name}.{field_name} includes"
+                        f" required argument {arg_name} that is missing from"
+                        f" the Interface field {iface.name}.{field_name}.",
+                        [type_arg.ast_node, iface_field.ast_node],
+                    )
+
+    def validate_type_implements_ancestors(
+        self,
+        type_: Union[GraphQLObjectType, GraphQLInterfaceType],
+        iface: GraphQLInterfaceType,
+    ) -> None:
+        type_interfaces, iface_interfaces = type_.interfaces, iface.interfaces
+        for transitive in iface_interfaces:
+            if transitive not in type_interfaces:
+                self.report_error(
+                    f"Type {type_.name} cannot implement {iface.name}"
+                    " because it would create a circular reference."
+                    if transitive is type_
+                    else f"Type {type_.name} must implement {transitive.name}"
+                    f" because it is implemented by {iface.name}.",
+                    get_all_implements_interface_nodes(iface, transitive)
+                    + get_all_implements_interface_nodes(type_, iface),
+                )
+
+    def validate_union_members(self, union: GraphQLUnionType) -> None:
+        member_types = union.types
+
+        if not member_types:
+            self.report_error(
+                f"Union type {union.name} must define one or more member types.",
+                get_all_nodes(union),
+            )
+
+        included_type_names: Set[str] = set()
+        for member_type in member_types:
+            if is_object_type(member_type):
+                if member_type.name in included_type_names:
+                    self.report_error(
+                        f"Union type {union.name} can only include type"
+                        f" {member_type.name} once.",
+                        get_union_member_type_nodes(union, member_type.name),
+                    )
+                else:
+                    included_type_names.add(member_type.name)
+            else:
+                self.report_error(
+                    f"Union type {union.name} can only include Object types,"
+                    f" it cannot include {inspect(member_type)}.",
+                    get_union_member_type_nodes(union, str(member_type)),
+                )
+
+    def validate_enum_values(self, enum_type: GraphQLEnumType) -> None:
+        enum_values = enum_type.values
+
+        if not enum_values:
+            self.report_error(
+                f"Enum type {enum_type.name} must define one or more values.",
+                get_all_nodes(enum_type),
+            )
+
+        for value_name, enum_value in enum_values.items():
+            # Ensure valid name.
+            self.validate_name(enum_value, value_name)
+            if value_name in ("true", "false", "null"):
+                self.report_error(
+                    f"Enum type {enum_type.name} cannot include value:"
+                    f" {value_name}.",
+                    enum_value.ast_node,
+                )
+
+    def validate_input_fields(self, input_obj: GraphQLInputObjectType) -> None:
+        fields = input_obj.fields
+
+        if not fields:
+            self.report_error(
+                f"Input Object type {input_obj.name}"
+                " must define one or more fields.",
+                get_all_nodes(input_obj),
+            )
+
+        # Ensure the arguments are valid
+        for field_name, field in fields.items():
+
+            # Ensure they are named correctly.
+            self.validate_name(field, field_name)
+
+            # Ensure the type is an input type.
+            if not is_input_type(field.type):
+                self.report_error(
+                    f"The type of {input_obj.name}.{field_name}"
+                    f" must be Input Type but got: {inspect(field.type)}.",
+                    field.ast_node.type if field.ast_node else None,
+                )
+
+
+def get_operation_type_node(
+    schema: GraphQLSchema, operation: OperationType
+) -> Optional[Node]:
+    operation_nodes = cast(
+        List[OperationTypeDefinitionNode],
+        get_all_sub_nodes(schema, attrgetter("operation_types")),
+    )
+    for node in operation_nodes:
+        if node.operation == operation:
+            return node.type
+    return None
+
+
+class InputObjectCircularRefsValidator:
+    """Modified copy of algorithm from validation.rules.NoFragmentCycles"""
+
+    def __init__(self, context: SchemaValidationContext):
+        self.context = context
+        # Tracks already visited types to maintain O(N) and to ensure that cycles
+        # are not redundantly reported.
+        self.visited_types: Set[str] = set()
+        # Array of input fields used to produce meaningful errors
+        self.field_path: List[Tuple[str, GraphQLInputField]] = []
+        # Position in the type path
+        self.field_path_index_by_type_name: Dict[str, int] = {}
+
+    def __call__(self, input_obj: GraphQLInputObjectType) -> None:
+        """Detect cycles recursively."""
+        # This does a straight-forward DFS to find cycles.
+        # It does not terminate when a cycle was found but continues to explore
+        # the graph to find all possible cycles.
+        name = input_obj.name
+        if name in self.visited_types:
+            return
+
+        self.visited_types.add(name)
+        self.field_path_index_by_type_name[name] = len(self.field_path)
+
+        for field_name, field in input_obj.fields.items():
+            if is_non_null_type(field.type) and is_input_object_type(
+                field.type.of_type
+            ):
+                field_type = cast(GraphQLInputObjectType, field.type.of_type)
+                cycle_index = self.field_path_index_by_type_name.get(field_type.name)
+
+                self.field_path.append((field_name, field))
+                if cycle_index is None:
+                    self(field_type)
+                else:
+                    cycle_path = self.field_path[cycle_index:]
+                    field_names = map(itemgetter(0), cycle_path)
+                    self.context.report_error(
+                        f"Cannot reference Input Object '{field_type.name}'"
+                        " within itself through a series of non-null fields:"
+                        f" '{'.'.join(field_names)}'.",
+                        cast(
+                            Collection[Node],
+                            map(attrgetter("ast_node"), map(itemgetter(1), cycle_path)),
+                        ),
+                    )
+                self.field_path.pop()
+
+        del self.field_path_index_by_type_name[name]
+
+
+SDLDefinedObject = Union[
+    GraphQLSchema,
+    GraphQLDirective,
+    GraphQLInterfaceType,
+    GraphQLObjectType,
+    GraphQLInputObjectType,
+    GraphQLUnionType,
+    GraphQLEnumType,
+]
+
+
+def get_all_nodes(obj: SDLDefinedObject) -> List[Node]:
+    node = obj.ast_node
+    nodes: List[Node] = [node] if node else []
+    extension_nodes = getattr(obj, "extension_ast_nodes", None)
+    if extension_nodes:
+        nodes.extend(extension_nodes)
+    return nodes
+
+
+def get_all_sub_nodes(
+    obj: SDLDefinedObject, getter: Callable[[Node], List[Node]]
+) -> List[Node]:
+    result: List[Node] = []
+    for ast_node in get_all_nodes(obj):
+        sub_nodes = getter(ast_node)
+        if sub_nodes:  # pragma: no cover
+            result.extend(sub_nodes)
+    return result
+
+
+def get_all_implements_interface_nodes(
+    type_: Union[GraphQLObjectType, GraphQLInterfaceType], iface: GraphQLInterfaceType
+) -> List[NamedTypeNode]:
+    implements_nodes = cast(
+        List[NamedTypeNode], get_all_sub_nodes(type_, attrgetter("interfaces"))
+    )
+    return [
+        iface_node
+        for iface_node in implements_nodes
+        if iface_node.name.value == iface.name
+    ]
+
+
+def get_union_member_type_nodes(
+    union: GraphQLUnionType, type_name: str
+) -> Optional[List[NamedTypeNode]]:
+    union_nodes = cast(
+        List[NamedTypeNode], get_all_sub_nodes(union, attrgetter("types"))
+    )
+    return [
+        union_node for union_node in union_nodes if union_node.name.value == type_name
+    ]
diff --git a/src/graphql/utilities/__init__.py b/src/graphql/utilities/__init__.py
new file mode 100644
index 0000000..4a978d3
--- /dev/null
+++ b/src/graphql/utilities/__init__.py
@@ -0,0 +1,124 @@
+"""GraphQL Utilities
+
+The :mod:`graphql.utilities` package contains common useful computations to use with
+the GraphQL language and type objects.
+"""
+
+# Produce the GraphQL query recommended for a full schema introspection.
+from .get_introspection_query import get_introspection_query
+
+# Get the target Operation from a Document.
+from .get_operation_ast import get_operation_ast
+
+# Get the Type for the target Operation AST.
+from .get_operation_root_type import get_operation_root_type
+
+# Convert a GraphQLSchema to an IntrospectionQuery.
+from .introspection_from_schema import introspection_from_schema
+
+# Build a GraphQLSchema from an introspection result.
+from .build_client_schema import build_client_schema
+
+# Build a GraphQLSchema from GraphQL Schema language.
+from .build_ast_schema import build_ast_schema, build_schema
+
+# Extend an existing GraphQLSchema from a parsed GraphQL Schema language AST.
+from .extend_schema import extend_schema, get_description
+
+# Sort a GraphQLSchema.
+from .lexicographic_sort_schema import lexicographic_sort_schema
+
+# Print a GraphQLSchema to GraphQL Schema language.
+from .print_schema import (
+    print_introspection_schema,
+    print_schema,
+    print_type,
+    print_value,
+)
+
+# Create a GraphQLType from a GraphQL language AST.
+from .type_from_ast import type_from_ast
+
+# Create a Python value from a GraphQL language AST with a type.
+from .value_from_ast import value_from_ast
+
+# Create a Python value from a GraphQL language AST without a type.
+from .value_from_ast_untyped import value_from_ast_untyped
+
+# Create a GraphQL language AST from a Python value.
+from .ast_from_value import ast_from_value
+
+# A helper to use within recursive-descent visitors which need to be aware of
+# the GraphQL type system
+from .type_info import TypeInfo, TypeInfoVisitor
+
+# Coerce a Python value to a GraphQL type, or produce errors.
+from .coerce_input_value import coerce_input_value
+
+# Concatenate multiple ASTs together.
+from .concat_ast import concat_ast
+
+# Separate an AST into an AST per Operation.
+from .separate_operations import separate_operations
+
+# Strip characters that are not significant to the validity or execution
+# of a GraphQL document.
+from .strip_ignored_characters import strip_ignored_characters
+
+# Comparators for types
+from .type_comparators import is_equal_type, is_type_sub_type_of, do_types_overlap
+
+# Assert that a string is a valid GraphQL name.
+from .assert_valid_name import assert_valid_name, is_valid_name_error
+
+# Compare two GraphQLSchemas and detect breaking changes.
+from .find_breaking_changes import (
+    BreakingChange,
+    BreakingChangeType,
+    DangerousChange,
+    DangerousChangeType,
+    find_breaking_changes,
+    find_dangerous_changes,
+)
+
+# Report all deprecated usages within a GraphQL document.
+from .find_deprecated_usages import find_deprecated_usages
+
+__all__ = [
+    "BreakingChange",
+    "BreakingChangeType",
+    "DangerousChange",
+    "DangerousChangeType",
+    "TypeInfo",
+    "TypeInfoVisitor",
+    "assert_valid_name",
+    "ast_from_value",
+    "build_ast_schema",
+    "build_client_schema",
+    "build_schema",
+    "coerce_input_value",
+    "concat_ast",
+    "do_types_overlap",
+    "extend_schema",
+    "find_breaking_changes",
+    "find_dangerous_changes",
+    "find_deprecated_usages",
+    "get_description",
+    "get_introspection_query",
+    "get_operation_ast",
+    "get_operation_root_type",
+    "is_equal_type",
+    "is_type_sub_type_of",
+    "is_valid_name_error",
+    "introspection_from_schema",
+    "lexicographic_sort_schema",
+    "print_introspection_schema",
+    "print_schema",
+    "print_type",
+    "print_value",
+    "separate_operations",
+    "strip_ignored_characters",
+    "type_from_ast",
+    "value_from_ast",
+    "value_from_ast_untyped",
+]
diff --git a/src/graphql/utilities/assert_valid_name.py b/src/graphql/utilities/assert_valid_name.py
new file mode 100644
index 0000000..ac6e156
--- /dev/null
+++ b/src/graphql/utilities/assert_valid_name.py
@@ -0,0 +1,33 @@
+import re
+from typing import Optional
+
+from ..error import GraphQLError
+
+__all__ = ["assert_valid_name", "is_valid_name_error"]
+
+
+re_name = re.compile("^[_a-zA-Z][_a-zA-Z0-9]*$")
+
+
+def assert_valid_name(name: str) -> str:
+    """Uphold the spec rules about naming."""
+    error = is_valid_name_error(name)
+    if error:
+        raise error
+    return name
+
+
+def is_valid_name_error(name: str) -> Optional[GraphQLError]:
+    """Return an Error if a name is invalid."""
+    if not isinstance(name, str):
+        raise TypeError("Expected name to be a string.")
+    if name.startswith("__"):
+        return GraphQLError(
+            f"Name {name!r} must not begin with '__',"
+            " which is reserved by GraphQL introspection."
+        )
+    if not re_name.match(name):
+        return GraphQLError(
+            f"Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but {name!r} does not."
+        )
+    return None
diff --git a/src/graphql/utilities/ast_from_value.py b/src/graphql/utilities/ast_from_value.py
new file mode 100644
index 0000000..c392e86
--- /dev/null
+++ b/src/graphql/utilities/ast_from_value.py
@@ -0,0 +1,136 @@
+import re
+from math import isfinite
+from typing import Any, Iterable, Mapping, Optional, cast
+
+from ..language import (
+    BooleanValueNode,
+    EnumValueNode,
+    FloatValueNode,
+    IntValueNode,
+    ListValueNode,
+    NameNode,
+    NullValueNode,
+    ObjectFieldNode,
+    ObjectValueNode,
+    StringValueNode,
+    ValueNode,
+)
+from ..pyutils import inspect, FrozenList, Undefined
+from ..type import (
+    GraphQLID,
+    GraphQLInputType,
+    GraphQLInputObjectType,
+    GraphQLList,
+    GraphQLNonNull,
+    is_enum_type,
+    is_input_object_type,
+    is_leaf_type,
+    is_list_type,
+    is_non_null_type,
+)
+
+__all__ = ["ast_from_value"]
+
+_re_integer_string = re.compile("^-?(?:0|[1-9][0-9]*)$")
+
+
+def ast_from_value(value: Any, type_: GraphQLInputType) -> Optional[ValueNode]:
+    """Produce a GraphQL Value AST given a Python object.
+
+    This function will match Python/JSON values to GraphQL AST schema format by using
+    the suggested GraphQLInputType. For example::
+
+        ast_from_value('value', GraphQLString)
+
+    A GraphQL type must be provided, which will be used to interpret different Python
+    values.
+
+    ================ =======================
+       JSON Value         GraphQL Value
+    ================ =======================
+       Object          Input Object
+       Array           List
+       Boolean         Boolean
+       String          String / Enum Value
+       Number          Int / Float
+       Mixed           Enum Value
+       null            NullValue
+    ================ =======================
+
+    """
+    if is_non_null_type(type_):
+        type_ = cast(GraphQLNonNull, type_)
+        ast_value = ast_from_value(value, type_.of_type)
+        if isinstance(ast_value, NullValueNode):
+            return None
+        return ast_value
+
+    # only explicit None, not Undefined or NaN
+    if value is None:
+        return NullValueNode()
+
+    # undefined
+    if value is Undefined:
+        return None
+
+    # Convert Python list to GraphQL list. If the GraphQLType is a list, but the value
+    # is not a list, convert the value using the list's item type.
+    if is_list_type(type_):
+        type_ = cast(GraphQLList, type_)
+        item_type = type_.of_type
+        if isinstance(value, Iterable) and not isinstance(value, str):
+            maybe_value_nodes = (ast_from_value(item, item_type) for item in value)
+            value_nodes = filter(None, maybe_value_nodes)
+            return ListValueNode(values=FrozenList(value_nodes))
+        return ast_from_value(value, item_type)
+
+    # Populate the fields of the input object by creating ASTs from each value in the
+    # Python dict according to the fields in the input type.
+    if is_input_object_type(type_):
+        if value is None or not isinstance(value, Mapping):
+            return None
+        type_ = cast(GraphQLInputObjectType, type_)
+        field_items = (
+            (field_name, ast_from_value(value[field_name], field.type))
+            for field_name, field in type_.fields.items()
+            if field_name in value
+        )
+        field_nodes = (
+            ObjectFieldNode(name=NameNode(value=field_name), value=field_value)
+            for field_name, field_value in field_items
+            if field_value
+        )
+        return ObjectValueNode(fields=FrozenList(field_nodes))
+
+    if is_leaf_type(type_):
+        # Since value is an internally represented value, it must be serialized to an
+        # externally represented value before converting into an AST.
+        serialized = type_.serialize(value)  # type: ignore
+        if serialized is None or serialized is Undefined:
+            return None
+
+        # Others serialize based on their corresponding Python scalar types.
+        if isinstance(serialized, bool):
+            return BooleanValueNode(value=serialized)
+
+        # Python ints and floats correspond nicely to Int and Float values.
+        if isinstance(serialized, int):
+            return IntValueNode(value=f"{serialized:d}")
+        if isinstance(serialized, float) and isfinite(serialized):
+            return FloatValueNode(value=f"{serialized:g}")
+
+        if isinstance(serialized, str):
+            # Enum types use Enum literals.
+            if is_enum_type(type_):
+                return EnumValueNode(value=serialized)
+
+            # ID types can use Int literals.
+            if type_ is GraphQLID and _re_integer_string.match(serialized):
+                return IntValueNode(value=serialized)
+
+            return StringValueNode(value=serialized)
+
+        raise TypeError(f"Cannot convert value to AST: {inspect(serialized)}.")
+
+    # Not reachable. All possible input types have been considered.
+    raise TypeError(f"Unexpected input type: {inspect(type_)}.")
diff --git a/src/graphql/utilities/build_ast_schema.py b/src/graphql/utilities/build_ast_schema.py
new file mode 100644
index 0000000..6e67e93
--- /dev/null
+++ b/src/graphql/utilities/build_ast_schema.py
@@ -0,0 +1,105 @@
+from typing import Union
+
+from ..language import (
+    DocumentNode,
+    Source,
+    parse,
+)
+from ..type import (
+    GraphQLDeprecatedDirective,
+    GraphQLIncludeDirective,
+    GraphQLSchema,
+    GraphQLSkipDirective,
+    GraphQLSpecifiedByDirective,
+)
+from .extend_schema import extend_schema_impl
+
+__all__ = [
+    "build_ast_schema",
+    "build_schema",
+]
+
+
+def build_ast_schema(
+    document_ast: DocumentNode,
+    assume_valid: bool = False,
+    assume_valid_sdl: bool = False,
+) -> GraphQLSchema:
+    """Build a GraphQL Schema from a given AST.
+
+    This takes the ast of a schema document produced by the parse function in
+    src/language/parser.py.
+
+    If no schema definition is provided, then it will look for types named Query
+    and Mutation.
+
+    Given that AST it constructs a GraphQLSchema. The resulting schema has no
+    resolve methods, so execution will use default resolvers.
+
+    When building a schema from a GraphQL service's introspection result, it might
+    be safe to assume the schema is valid. Set ``assume_valid`` to ``True`` to assume
+    the produced schema is valid. Set ``assume_valid_sdl`` to ``True`` to assume it is
+    already a valid SDL document.
+    """
+    if not isinstance(document_ast, DocumentNode):
+        raise TypeError("Must provide valid Document AST.")
+
+    if not (assume_valid or assume_valid_sdl):
+        from ..validation.validate import assert_valid_sdl
+
+        assert_valid_sdl(document_ast)
+
+    empty_schema_config = GraphQLSchema(
+        description=None,
+        types=[],
+        directives=[],
+        extensions=None,
+        extension_ast_nodes=[],
+        assume_valid=False,
+    ).to_kwargs()
+    schema_kwargs = extend_schema_impl(empty_schema_config, document_ast, assume_valid)
+
+    if not schema_kwargs["ast_node"]:
+        for type_ in schema_kwargs["types"]:
+            # Note: While this could make early assertions to get the correctly
+            # typed values below, that would throw immediately while type system
+            # validation with validate_schema() will produce more actionable results.
+            type_name = type_.name
+            if type_name == "Query":
+                schema_kwargs["query"] = type_
+            elif type_name == "Mutation":
+                schema_kwargs["mutation"] = type_
+            elif type_name == "Subscription":
+                schema_kwargs["subscription"] = type_
+
+    directives = schema_kwargs["directives"]
+    # If specified directives were not explicitly declared, add them.
+    if not any(directive.name == "skip" for directive in directives):
+        directives.append(GraphQLSkipDirective)
+    if not any(directive.name == "include" for directive in directives):
+        directives.append(GraphQLIncludeDirective)
+    if not any(directive.name == "deprecated" for directive in directives):
+        directives.append(GraphQLDeprecatedDirective)
+    if not any(directive.name == "specifiedBy" for directive in directives):
+        directives.append(GraphQLSpecifiedByDirective)
+
+    return GraphQLSchema(**schema_kwargs)
+
+
+def build_schema(
+    source: Union[str, Source],
+    assume_valid: bool = False,
+    assume_valid_sdl: bool = False,
+    no_location: bool = False,
+    experimental_fragment_variables: bool = False,
+) -> GraphQLSchema:
+    """Build a GraphQLSchema directly from a source document."""
+    return build_ast_schema(
+        parse(
+            source,
+            no_location=no_location,
+            experimental_fragment_variables=experimental_fragment_variables,
+        ),
+        assume_valid=assume_valid,
+        assume_valid_sdl=assume_valid_sdl,
+    )
diff --git a/src/graphql/utilities/build_client_schema.py b/src/graphql/utilities/build_client_schema.py
new file mode 100644
index 0000000..f02dc28
--- /dev/null
+++ b/src/graphql/utilities/build_client_schema.py
@@ -0,0 +1,375 @@
+from itertools import chain
+from typing import cast, Callable, Collection, Dict, List
+
+from ..language import DirectiveLocation, parse_value
+from ..pyutils import inspect, Undefined
+from ..type import (
+    GraphQLArgument,
+    GraphQLDirective,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInputType,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNamedType,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLOutputType,
+    GraphQLScalarType,
+    GraphQLSchema,
+    GraphQLType,
+    GraphQLUnionType,
+    TypeKind,
+    assert_interface_type,
+    assert_nullable_type,
+    assert_object_type,
+    introspection_types,
+    is_input_type,
+    is_output_type,
+    specified_scalar_types,
+)
+from .value_from_ast import value_from_ast
+
+__all__ = ["build_client_schema"]
+
+
+def build_client_schema(
+    introspection: Dict, assume_valid: bool = False
+) -> GraphQLSchema:
+    """Build a GraphQLSchema for use by client tools.
+
+    Given the result of a client running the introspection query, creates and returns
+    a GraphQLSchema instance which can be then used with all GraphQL-core 3 tools,
+    but cannot be used to execute a query, as introspection does not represent the
+    "resolver", "parse" or "serialize" functions or any other server-internal
+    mechanisms.
+
+    This function expects a complete introspection result. Don't forget to check the
+    "errors" field of a server response before calling this function.
+    """
+    if not isinstance(introspection, dict) or not isinstance(
+        introspection.get("__schema"), dict
+    ):
+        raise TypeError(
+            "Invalid or incomplete introspection result. Ensure that you"
+            " are passing the 'data' attribute of an introspection response"
+            f" and no 'errors' were returned alongside: {inspect(introspection)}."
+        )
+
+    # Get the schema from the introspection result.
+    schema_introspection = introspection["__schema"]
+
+    # Given a type reference in introspection, return the GraphQLType instance,
+    # preferring cached instances before building new instances.
+    def get_type(type_ref: Dict) -> GraphQLType:
+        kind = type_ref.get("kind")
+        if kind == TypeKind.LIST.name:
+            item_ref = type_ref.get("ofType")
+            if not item_ref:
+                raise TypeError("Decorated type deeper than introspection query.")
+            return GraphQLList(get_type(item_ref))
+        elif kind == TypeKind.NON_NULL.name:
+            nullable_ref = type_ref.get("ofType")
+            if not nullable_ref:
+                raise TypeError("Decorated type deeper than introspection query.")
+            nullable_type = get_type(nullable_ref)
+            return GraphQLNonNull(assert_nullable_type(nullable_type))
+        return get_named_type(type_ref)
+
+    def get_named_type(type_ref: Dict) -> GraphQLNamedType:
+        type_name = type_ref.get("name")
+        if not type_name:
+            raise TypeError(f"Unknown type reference: {inspect(type_ref)}.")
+
+        type_ = type_map.get(type_name)
+        if not type_:
+            raise TypeError(
+                f"Invalid or incomplete schema, unknown type: {type_name}."
+                " Ensure that a full introspection query is used in order"
+                " to build a client schema."
+            )
+        return type_
+
+    def get_object_type(type_ref: Dict) -> GraphQLObjectType:
+        return assert_object_type(get_type(type_ref))
+
+    def get_interface_type(type_ref: Dict) -> GraphQLInterfaceType:
+        return assert_interface_type(get_type(type_ref))
+
+    # Given a type's introspection result, construct the correct GraphQLType instance.
+    def build_type(type_: Dict) -> GraphQLNamedType:
+        if type_ and "name" in type_ and "kind" in type_:
+            builder = type_builders.get(cast(str, type_["kind"]))
+            if builder:  # pragma: no cover else
+                return cast(GraphQLNamedType, builder(type_))
+        raise TypeError(
+            "Invalid or incomplete introspection result."
+            " Ensure that a full introspection query is used in order"
+            f" to build a client schema: {inspect(type_)}."
+        )
+
+    def build_scalar_def(scalar_introspection: Dict) -> GraphQLScalarType:
+        return GraphQLScalarType(
+            name=scalar_introspection["name"],
+            description=scalar_introspection.get("description"),
+            specified_by_url=scalar_introspection.get("specifiedByUrl"),
+        )
+
+    def build_implementations_list(
+        implementing_introspection: Dict,
+    ) -> List[GraphQLInterfaceType]:
+        interfaces = implementing_introspection.get("interfaces")
+        if interfaces is None:
+            # Temporary workaround until GraphQL ecosystem will fully support
+            # 'interfaces' on interface types
+            if implementing_introspection["kind"] == TypeKind.INTERFACE.name:
+                return []
+            raise TypeError(
+                "Introspection result missing interfaces:"
+                f" {inspect(implementing_introspection)}."
+            )
+        return [get_interface_type(interface) for interface in interfaces]
+
+    def build_object_def(object_introspection: Dict) -> GraphQLObjectType:
+        return GraphQLObjectType(
+            name=object_introspection["name"],
+            description=object_introspection.get("description"),
+            interfaces=lambda: build_implementations_list(object_introspection),
+            fields=lambda: build_field_def_map(object_introspection),
+        )
+
+    def build_interface_def(interface_introspection: Dict) -> GraphQLInterfaceType:
+        return GraphQLInterfaceType(
+            name=interface_introspection["name"],
+            description=interface_introspection.get("description"),
+            interfaces=lambda: build_implementations_list(interface_introspection),
+            fields=lambda: build_field_def_map(interface_introspection),
+        )
+
+    def build_union_def(union_introspection: Dict) -> GraphQLUnionType:
+        possible_types = union_introspection.get("possibleTypes")
+        if possible_types is None:
+            raise TypeError(
+                "Introspection result missing possibleTypes:"
+                f" {inspect(union_introspection)}."
+            )
+        return GraphQLUnionType(
+            name=union_introspection["name"],
+            description=union_introspection.get("description"),
+            types=lambda: [
+                get_object_type(type_) for type_ in cast(List[Dict], possible_types)
+            ],
+        )
+
+    def build_enum_def(enum_introspection: Dict) -> GraphQLEnumType:
+        if enum_introspection.get("enumValues") is None:
+            raise TypeError(
+                "Introspection result missing enumValues:"
+                f" {inspect(enum_introspection)}."
+            )
+        return GraphQLEnumType(
+            name=enum_introspection["name"],
+            description=enum_introspection.get("description"),
+            values={
+                value_introspect["name"]: GraphQLEnumValue(
+                    description=value_introspect.get("description"),
+                    deprecation_reason=value_introspect.get("deprecationReason"),
+                )
+                for value_introspect in enum_introspection["enumValues"]
+            },
+        )
+
+    def build_input_object_def(
+        input_object_introspection: Dict,
+    ) -> GraphQLInputObjectType:
+        if input_object_introspection.get("inputFields") is None:
+            raise TypeError(
+                "Introspection result missing inputFields:"
+                f" {inspect(input_object_introspection)}."
+            )
+        return GraphQLInputObjectType(
+            name=input_object_introspection["name"],
+            description=input_object_introspection.get("description"),
+            fields=lambda: build_input_value_def_map(
+                input_object_introspection["inputFields"]
+            ),
+        )
+
+    type_builders: Dict[str, Callable[[Dict], GraphQLType]] = {
+        TypeKind.SCALAR.name: build_scalar_def,
+        TypeKind.OBJECT.name: build_object_def,
+        TypeKind.INTERFACE.name: build_interface_def,
+        TypeKind.UNION.name: build_union_def,
+        TypeKind.ENUM.name: build_enum_def,
+        TypeKind.INPUT_OBJECT.name: build_input_object_def,
+    }
+
+    def build_field_def_map(type_introspection: Dict) -> Dict[str, GraphQLField]:
+        if type_introspection.get("fields") is None:
+            raise TypeError(
+                f"Introspection result missing fields: {type_introspection}."
+            )
+        return {
+            field_introspection["name"]: build_field(field_introspection)
+            for field_introspection in type_introspection["fields"]
+        }
+
+    def build_field(field_introspection: Dict) -> GraphQLField:
+        type_ = get_type(field_introspection["type"])
+        if not is_output_type(type_):
+            raise TypeError(
+                "Introspection must provide output type for fields,"
+                f" but received: {inspect(type_)}."
+            )
+        type_ = cast(GraphQLOutputType, type_)
+
+        args_introspection = field_introspection.get("args")
+        if args_introspection is None:
+            raise TypeError(
+                "Introspection result missing field args:"
+                f" {inspect(field_introspection)}."
+            )
+
+        return GraphQLField(
+            type_,
+            args=build_argument_def_map(args_introspection),
+            description=field_introspection.get("description"),
+            deprecation_reason=field_introspection.get("deprecationReason"),
+        )
+
+    def build_argument_def_map(
+        input_value_introspections: Dict,
+    ) -> Dict[str, GraphQLArgument]:
+        return {
+            argument_introspection["name"]: build_argument(argument_introspection)
+            for argument_introspection in input_value_introspections
+        }
+
+    def build_argument(argument_introspection: Dict) -> GraphQLArgument:
+        type_ = get_type(argument_introspection["type"])
+        if not is_input_type(type_):
+            raise TypeError(
+                "Introspection must provide input type for arguments,"
+                f" but received: {inspect(type_)}."
+            )
+        type_ = cast(GraphQLInputType, type_)
+
+        default_value = argument_introspection.get("defaultValue")
+        default_value = (
+            Undefined
+            if default_value is None
+            else value_from_ast(parse_value(default_value), type_)
+        )
+        return GraphQLArgument(
+            type_,
+            default_value=default_value,
+            description=argument_introspection.get("description"),
+        )
+
+    def build_input_value_def_map(
+        input_value_introspections: Dict,
+    ) -> Dict[str, GraphQLInputField]:
+        return {
+            input_value_introspection["name"]: build_input_value(
+                input_value_introspection
+            )
+            for input_value_introspection in input_value_introspections
+        }
+
+    def build_input_value(input_value_introspection: Dict) -> GraphQLInputField:
+        type_ = get_type(input_value_introspection["type"])
+        if not is_input_type(type_):
+            raise TypeError(
+                "Introspection must provide input type for input fields,"
+                f" but received: {inspect(type_)}."
+            )
+        type_ = cast(GraphQLInputType, type_)
+
+        default_value = input_value_introspection.get("defaultValue")
+        default_value = (
+            Undefined
+            if default_value is None
+            else value_from_ast(parse_value(default_value), type_)
+        )
+        return GraphQLInputField(
+            type_,
+            default_value=default_value,
+            description=input_value_introspection.get("description"),
+        )
+
+    def build_directive(directive_introspection: Dict) -> GraphQLDirective:
+        if directive_introspection.get("args") is None:
+            raise TypeError(
+                "Introspection result missing directive args:"
+                f" {inspect(directive_introspection)}."
+            )
+        if directive_introspection.get("locations") is None:
+            raise TypeError(
+                "Introspection result missing directive locations:"
+                f" {inspect(directive_introspection)}."
+            )
+        return GraphQLDirective(
+            name=directive_introspection["name"],
+            description=directive_introspection.get("description"),
+            is_repeatable=directive_introspection.get("isRepeatable", False),
+            locations=list(
+                cast(
+                    Collection[DirectiveLocation],
+                    directive_introspection.get("locations"),
+                )
+            ),
+            args=build_argument_def_map(directive_introspection["args"]),
+        )
+
+    # Iterate through all types, getting the type definition for each.
+    type_map: Dict[str, GraphQLNamedType] = {
+        type_introspection["name"]: build_type(type_introspection)
+        for type_introspection in schema_introspection["types"]
+    }
+
+    # Include standard types only if they are used.
+    for std_type_name, std_type in chain(
+        specified_scalar_types.items(), introspection_types.items()
+    ):
+        if std_type_name in type_map:
+            type_map[std_type_name] = std_type
+
+    # Get the root Query, Mutation, and Subscription types.
+    query_type_ref = schema_introspection.get("queryType")
+    query_type = None if query_type_ref is None else get_object_type(query_type_ref)
+    mutation_type_ref = schema_introspection.get("mutationType")
+    mutation_type = (
+        None if mutation_type_ref is None else get_object_type(mutation_type_ref)
+    )
+    subscription_type_ref = schema_introspection.get("subscriptionType")
+    subscription_type = (
+        None
+        if subscription_type_ref is None
+        else get_object_type(subscription_type_ref)
+    )
+
+    # Get the directives supported by Introspection, assuming empty-set if directives
+    # were not queried for.
+    directive_introspections = schema_introspection.get("directives")
+    directives = (
+        [
+            build_directive(directive_introspection)
+            for directive_introspection in directive_introspections
+        ]
+        if directive_introspections
+        else []
+    )
+
+    # Then produce and return a Schema with these types.
+    return GraphQLSchema(
+        query=query_type,
+        mutation=mutation_type,
+        subscription=subscription_type,
+        types=list(type_map.values()),
+        directives=directives,
+        description=schema_introspection.get("description"),
+        assume_valid=assume_valid,
+    )
diff --git a/src/graphql/utilities/coerce_input_value.py b/src/graphql/utilities/coerce_input_value.py
new file mode 100644
index 0000000..c48c51e
--- /dev/null
+++ b/src/graphql/utilities/coerce_input_value.py
@@ -0,0 +1,158 @@
+from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast
+
+
+from ..error import GraphQLError
+from ..pyutils import (
+    Path,
+    did_you_mean,
+    inspect,
+    print_path_list,
+    suggestion_list,
+    Undefined,
+)
+from ..type import (
+    GraphQLInputObjectType,
+    GraphQLInputType,
+    GraphQLList,
+    GraphQLScalarType,
+    is_leaf_type,
+    is_input_object_type,
+    is_list_type,
+    is_non_null_type,
+    GraphQLNonNull,
+)
+
+__all__ = ["coerce_input_value"]
+
+
+OnErrorCB = Callable[[List[Union[str, int]], Any, GraphQLError], None]
+
+
+def default_on_error(
+    path: List[Union[str, int]], invalid_value: Any, error: GraphQLError
+) -> None:
+    error_prefix = "Invalid value " + inspect(invalid_value)
+    if path:
+        error_prefix += f" at 'value{print_path_list(path)}'"
+    error.message = error_prefix + ": " + error.message
+    raise error
+
+
+def coerce_input_value(
+    input_value: Any,
+    type_: GraphQLInputType,
+    on_error: OnErrorCB = default_on_error,
+    path: Optional[Path] = None,
+) -> Any:
+    """Coerce a Python value given a GraphQL Input Type."""
+    if is_non_null_type(type_):
+        if input_value is not None and input_value is not Undefined:
+            type_ = cast(GraphQLNonNull, type_)
+            return coerce_input_value(input_value, type_.of_type, on_error, path)
+        on_error(
+            path.as_list() if path else [],
+            input_value,
+            GraphQLError(
+                f"Expected non-nullable type '{inspect(type_)}' not to be None."
+            ),
+        )
+        return Undefined
+
+    if input_value is None or input_value is Undefined:
+        # Explicitly return the value null.
+        return None
+
+    if is_list_type(type_):
+        type_ = cast(GraphQLList, type_)
+        item_type = type_.of_type
+        if isinstance(input_value, Iterable) and not isinstance(input_value, str):
+            coerced_list: List[Any] = []
+            append_item = coerced_list.append
+            for index, item_value in enumerate(input_value):
+                append_item(
+                    coerce_input_value(
+                        item_value, item_type, on_error, Path(path, index)
+                    )
+                )
+            return coerced_list
+        # Lists accept a non-list value as a list of one.
+        return [coerce_input_value(input_value, item_type, on_error, path)]
+
+    if is_input_object_type(type_):
+        type_ = cast(GraphQLInputObjectType, type_)
+        if not isinstance(input_value, dict):
+            on_error(
+                path.as_list() if path else [],
+                input_value,
+                GraphQLError(f"Expected type '{type_.name}' to be a dict."),
+            )
+            return Undefined
+
+        coerced_dict: Dict[str, Any] = {}
+        fields = type_.fields
+
+        for field_name, field in fields.items():
+            field_value = input_value.get(field_name, Undefined)
+
+            if field_value is Undefined:
+                if field.default_value is not Undefined:
+                    # Use out name as name if it exists (extension of GraphQL.js).
+                    coerced_dict[field.out_name or field_name] = field.default_value
+                elif is_non_null_type(field.type):  # pragma: no cover else
+                    type_str = inspect(field.type)
+                    on_error(
+                        path.as_list() if path else [],
+                        input_value,
+                        GraphQLError(
+                            f"Field '{field_name}' of required type '{type_str}'"
+                            " was not provided."
+                        ),
+                    )
+                continue
+
+            coerced_dict[field.out_name or field_name] = coerce_input_value(
+                field_value, field.type, on_error, Path(path, field_name)
+            )
+
+        # Ensure every provided field is defined.
+        for field_name in input_value:
+            if field_name not in fields:
+                suggestions = suggestion_list(field_name, fields)
+                on_error(
+                    path.as_list() if path else [],
+                    input_value,
+                    GraphQLError(
+                        f"Field '{field_name}' is not defined by type '{type_.name}'."
+                        + did_you_mean(suggestions)
+                    ),
+                )
+        return type_.out_type(coerced_dict)
+
+    if is_leaf_type(type_):
+        # Scalars determine if a value is valid via `parse_value()`, which can throw to
+        # indicate failure. If it throws, maintain a reference to the original error.
+        type_ = cast(GraphQLScalarType, type_)
+        try:
+            parse_result = type_.parse_value(input_value)
+        except GraphQLError as error:
+            on_error(path.as_list() if path else [], input_value, error)
+            return Undefined
+        except Exception as error:
+            on_error(
+                path.as_list() if path else [],
+                input_value,
+                GraphQLError(
+                    f"Expected type '{type_.name}'. {error}", original_error=error
+                ),
+            )
+            return Undefined
+        if parse_result is Undefined:
+            on_error(
+                path.as_list() if path else [],
+                input_value,
+                GraphQLError(f"Expected type '{type_.name}'."),
+            )
+        return parse_result
+
+    # Not reachable. All possible input types have been considered.
+    raise TypeError(f"Unexpected input type: {inspect(type_)}.")
diff --git a/src/graphql/utilities/concat_ast.py b/src/graphql/utilities/concat_ast.py
new file mode 100644
index 0000000..cd12a74
--- /dev/null
+++ b/src/graphql/utilities/concat_ast.py
@@ -0,0 +1,18 @@
+from itertools import chain
+from typing import Collection
+
+from ..language.ast import DocumentNode
+
+__all__ = ["concat_ast"]
+
+
+def concat_ast(asts: Collection[DocumentNode]) -> DocumentNode:
+    """Concat ASTs.
+
+    Provided a collection of ASTs, presumably each from different files, concatenate
+    the ASTs together into batched AST, useful for validating many GraphQL source files
+    which together represent one conceptual application.
+    """
+    return DocumentNode(
+        definitions=list(chain.from_iterable(document.definitions for document in asts))
+    )
diff --git a/src/graphql/utilities/extend_schema.py b/src/graphql/utilities/extend_schema.py
new file mode 100644
index 0000000..71cf630
--- /dev/null
+++ b/src/graphql/utilities/extend_schema.py
@@ -0,0 +1,702 @@
+from collections import defaultdict
+from typing import (
+    Any,
+    Callable,
+    Collection,
+    DefaultDict,
+    Dict,
+    List,
+    Optional,
+    Union,
+    cast,
+)
+
+from ..language import (
+    DirectiveDefinitionNode,
+    DirectiveLocation,
+    DocumentNode,
+    EnumTypeDefinitionNode,
+    EnumTypeExtensionNode,
+    EnumValueDefinitionNode,
+    FieldDefinitionNode,
+    InputObjectTypeDefinitionNode,
+    InputObjectTypeExtensionNode,
+    InputValueDefinitionNode,
+    InterfaceTypeDefinitionNode,
+    InterfaceTypeExtensionNode,
+    ListTypeNode,
+    NamedTypeNode,
+    Node,
+    NonNullTypeNode,
+    ObjectTypeDefinitionNode,
+    ObjectTypeExtensionNode,
+    OperationType,
+    ScalarTypeDefinitionNode,
+    ScalarTypeExtensionNode,
+    SchemaExtensionNode,
+    SchemaDefinitionNode,
+    TypeDefinitionNode,
+    TypeExtensionNode,
+    TypeNode,
+    UnionTypeDefinitionNode,
+    UnionTypeExtensionNode,
+)
+from ..pyutils import inspect
+from ..type import (
+    GraphQLArgument,
+    GraphQLArgumentMap,
+    GraphQLDeprecatedDirective,
+    GraphQLDirective,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLEnumValueMap,
+    GraphQLField,
+    GraphQLFieldMap,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInputType,
+    GraphQLInputFieldMap,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNamedType,
+    GraphQLNonNull,
+    GraphQLNullableType,
+    GraphQLObjectType,
+    GraphQLOutputType,
+    GraphQLScalarType,
+    GraphQLSchema,
+    GraphQLSpecifiedByDirective,
+    GraphQLType,
+    GraphQLUnionType,
+    assert_schema,
+    is_enum_type,
+    is_input_object_type,
+    is_interface_type,
+    is_list_type,
+    is_non_null_type,
+    is_object_type,
+    is_scalar_type,
+    is_union_type,
+    is_introspection_type,
+    is_specified_scalar_type,
+    introspection_types,
+    specified_scalar_types,
+)
+from .value_from_ast import value_from_ast
+
+__all__ = [
+    "extend_schema",
+    "extend_schema_impl",
+    "get_description",
+]
+
+
+def extend_schema(
+    schema: GraphQLSchema,
+    document_ast: DocumentNode,
+    assume_valid: bool = False,
+    assume_valid_sdl: bool = False,
+) -> GraphQLSchema:
+    """Extend the schema with extensions from a given document.
+
+    Produces a new schema given an existing schema and a document which may contain
+    GraphQL type extensions and definitions. The original schema will remain unaltered.
+
+    Because a schema represents a graph of references, a schema cannot be extended
+    without effectively making an entire copy. We do not know until it's too late if
+    subgraphs remain unchanged.
+
+    This algorithm copies the provided schema, applying extensions while producing the
+    copy. The original schema remains unaltered.
+
+    When extending a schema with a known valid extension, it might be safe to assume the
+    schema is valid. Set ``assume_valid`` to ``True`` to assume the produced schema is
+    valid. Set ``assume_valid_sdl`` to ``True`` to assume it is already a valid SDL
+    document.
+    """
+    assert_schema(schema)
+
+    if not isinstance(document_ast, DocumentNode):
+        raise TypeError("Must provide valid Document AST.")
+
+    if not (assume_valid or assume_valid_sdl):
+        from ..validation.validate import assert_valid_sdl_extension
+
+        assert_valid_sdl_extension(document_ast, schema)
+
+    schema_kwargs = schema.to_kwargs()
+    extended_kwargs = extend_schema_impl(schema_kwargs, document_ast, assume_valid)
+    return (
+        schema if schema_kwargs is extended_kwargs else GraphQLSchema(**extended_kwargs)
+    )
+
+
+def extend_schema_impl(
+    schema_kwargs: Dict[str, Any],
+    document_ast: DocumentNode,
+    assume_valid: bool = False,
+) -> Dict[str, Any]:
+    """Extend the given schema arguments with extensions from a given document.
+
+    For internal use only.
+    """
+    # Note: schema_kwargs should become a TypedDict once we require Python 3.8
+
+    # Collect the type definitions and extensions found in the document.
+    type_defs: List[TypeDefinitionNode] = []
+    type_extensions_map: DefaultDict[str, Any] = defaultdict(list)
+
+    # New directives and types are separate because a directives and types can have the
+    # same name. For example, a type named "skip".
+    directive_defs: List[DirectiveDefinitionNode] = []
+
+    schema_def: Optional[SchemaDefinitionNode] = None
+    # Schema extensions are collected which may add additional operation types.
+    schema_extensions: List[SchemaExtensionNode] = []
+
+    for def_ in document_ast.definitions:
+        if isinstance(def_, SchemaDefinitionNode):
+            schema_def = def_
+        elif isinstance(def_, SchemaExtensionNode):
+            schema_extensions.append(def_)
+        elif isinstance(def_, TypeDefinitionNode):
+            type_defs.append(def_)
+        elif isinstance(def_, TypeExtensionNode):
+            extended_type_name = def_.name.value
+            type_extensions_map[extended_type_name].append(def_)
+        elif isinstance(def_, DirectiveDefinitionNode):
+            directive_defs.append(def_)
+
+    # If this document contains no new types, extensions, or directives then return the
+    # same unmodified GraphQLSchema instance.
+    if (
+        not type_extensions_map
+        and not type_defs
+        and not directive_defs
+        and not schema_extensions
+        and not schema_def
+    ):
+        return schema_kwargs
+
+    # Below are functions used for producing this schema that have closed over this
+    # scope and have access to the schema, cache, and newly defined types.
+
+    # noinspection PyTypeChecker,PyUnresolvedReferences
+    def replace_type(type_: GraphQLType) -> GraphQLType:
+        if is_list_type(type_):
+            return GraphQLList(replace_type(type_.of_type))  # type: ignore
+        if is_non_null_type(type_):
+            return GraphQLNonNull(replace_type(type_.of_type))  # type: ignore
+        return replace_named_type(type_)  # type: ignore
+
+    def replace_named_type(type_: GraphQLNamedType) -> GraphQLNamedType:
+        # Note: While this could make early assertions to get the correctly
+        # typed values below, that would throw immediately while type system
+        # validation with validate_schema() will produce more actionable results.
+        return type_map[type_.name]
+
+    # noinspection PyShadowingNames
+    def replace_directive(directive: GraphQLDirective) -> GraphQLDirective:
+        kwargs = directive.to_kwargs()
+        return GraphQLDirective(
+            **{  # type: ignore
+                **kwargs,
+                "args": {name: extend_arg(arg) for name, arg in kwargs["args"].items()},
+            }
+        )
+
+    def extend_named_type(type_: GraphQLNamedType) -> GraphQLNamedType:
+        if is_introspection_type(type_) or is_specified_scalar_type(type_):
+            # Builtin types are not extended.
+            return type_
+        if is_scalar_type(type_):
+            type_ = cast(GraphQLScalarType, type_)
+            return extend_scalar_type(type_)
+        if is_object_type(type_):
+            type_ = cast(GraphQLObjectType, type_)
+            return extend_object_type(type_)
+        if is_interface_type(type_):
+            type_ = cast(GraphQLInterfaceType, type_)
+            return extend_interface_type(type_)
+        if is_union_type(type_):
+            type_ = cast(GraphQLUnionType, type_)
+            return extend_union_type(type_)
+        if is_enum_type(type_):
+            type_ = cast(GraphQLEnumType, type_)
+            return extend_enum_type(type_)
+        if is_input_object_type(type_):
+            type_ = cast(GraphQLInputObjectType, type_)
+            return extend_input_object_type(type_)
+
+        # Not reachable. All possible types have been considered.
+        raise TypeError(f"Unexpected type: {inspect(type_)}.")  # pragma: no cover
+
+    # noinspection PyShadowingNames
+    def extend_input_object_type(
+        type_: GraphQLInputObjectType,
+    ) -> GraphQLInputObjectType:
+        kwargs = type_.to_kwargs()
+        extensions = type_extensions_map[kwargs["name"]]
+
+        return GraphQLInputObjectType(
+            **{
+                **kwargs,
+                "fields": lambda: {
+                    **{
+                        name: GraphQLInputField(
+                            **{  # type: ignore
+                                **field.to_kwargs(),
+                                "type_": replace_type(field.type),
+                            }
+                        )
+                        for name, field in kwargs["fields"].items()
+                    },
+                    **build_input_field_map(extensions),
+                },
+                "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions,
+            }
+        )
+
+    def extend_enum_type(type_: GraphQLEnumType) -> GraphQLEnumType:
+        kwargs = type_.to_kwargs()
+        extensions = type_extensions_map[kwargs["name"]]
+
+        return GraphQLEnumType(
+            **{
+                **kwargs,
+                "values": {**kwargs["values"], **build_enum_value_map(extensions)},
+                "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions,
+            }
+        )
+
+    def extend_scalar_type(type_: GraphQLScalarType) -> GraphQLScalarType:
+        kwargs = type_.to_kwargs()
+        extensions = type_extensions_map[kwargs["name"]]
+
+        specified_by_url = kwargs["specified_by_url"]
+        for extension_node in extensions:
+            specified_by_url = get_specified_by_url(extension_node) or specified_by_url
+
+        return GraphQLScalarType(
+            **{
+                **kwargs,
+                "specified_by_url": specified_by_url,
+                "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions,
+            }
+        )
+
+    # noinspection PyShadowingNames
+    def extend_object_type(type_: GraphQLObjectType) -> GraphQLObjectType:
+        kwargs = type_.to_kwargs()
+        extensions = type_extensions_map[kwargs["name"]]
+
+        return GraphQLObjectType(
+            **{
+                **kwargs,
+                "interfaces": lambda: [
+                    cast(GraphQLInterfaceType, replace_named_type(interface))
+                    for interface in kwargs["interfaces"]
+                ]
+                + build_interfaces(extensions),
+                "fields": lambda: {
+                    **{
+                        name: extend_field(field)
+                        for name, field in kwargs["fields"].items()
+                    },
+                    **build_field_map(extensions),
+                },
+                "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions,
+            }
+        )
+
+    # noinspection PyShadowingNames
+    def extend_interface_type(type_: GraphQLInterfaceType) -> GraphQLInterfaceType:
+        kwargs = type_.to_kwargs()
+        extensions = type_extensions_map[kwargs["name"]]
+
+        return GraphQLInterfaceType(
+            **{
+                **kwargs,
+                "interfaces": lambda: [
+                    cast(GraphQLInterfaceType, replace_named_type(interface))
+                    for interface in kwargs["interfaces"]
+                ]
+                + build_interfaces(extensions),
+                "fields": lambda: {
+                    **{
+                        name: extend_field(field)
+                        for name, field in kwargs["fields"].items()
+                    },
+                    **build_field_map(extensions),
+                },
+                "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions,
+            }
+        )
+
+    def extend_union_type(type_: GraphQLUnionType) -> GraphQLUnionType:
+        kwargs = type_.to_kwargs()
+        extensions = type_extensions_map[kwargs["name"]]
+
+        return GraphQLUnionType(
+            **{
+                **kwargs,
+                "types": lambda: [
+                    cast(GraphQLObjectType, replace_named_type(member_type))
+                    for member_type in kwargs["types"]
+                ]
+                + build_union_types(extensions),
+                "extension_ast_nodes": kwargs["extension_ast_nodes"] + extensions,
+            }
+        )
+
+    # noinspection PyShadowingNames
+    def extend_field(field: GraphQLField) -> GraphQLField:
+        return GraphQLField(
+            **{  # type: ignore
+                **field.to_kwargs(),
+                "type_": replace_type(field.type),
+                "args": {name: extend_arg(arg) for name, arg in field.args.items()},
+            }
+        )
+
+    def extend_arg(arg: GraphQLArgument) -> GraphQLArgument:
+        return GraphQLArgument(
+            **{  # type: ignore
+                **arg.to_kwargs(),
+                "type_": replace_type(arg.type),
+            }
+        )
+
+    # noinspection PyShadowingNames
+    def get_operation_types(
+        nodes: Collection[Union[SchemaDefinitionNode, SchemaExtensionNode]]
+    ) -> Dict[OperationType, GraphQLNamedType]:
+        # Note: While this could make early assertions to get the correctly
+        # typed values below, that would throw immediately while type system
+        # validation with validate_schema() will produce more actionable results.
+        return {
+            operation_type.operation: get_named_type(operation_type.type)
+            for node in nodes
+            for operation_type in node.operation_types or []
+        }
+
+    # noinspection PyShadowingNames
+    def get_named_type(node: NamedTypeNode) -> GraphQLNamedType:
+        name = node.name.value
+        type_ = std_type_map.get(name) or type_map.get(name)
+
+        if not type_:
+            raise TypeError(f"Unknown type: '{name}'.")
+        return type_
+
+    def get_wrapped_type(node: TypeNode) -> GraphQLType:
+        if isinstance(node, ListTypeNode):
+            return GraphQLList(get_wrapped_type(node.type))
+        if isinstance(node, NonNullTypeNode):
+            return GraphQLNonNull(
+                cast(GraphQLNullableType, get_wrapped_type(node.type))
+            )
+        return get_named_type(cast(NamedTypeNode, node))
+
+    def build_directive(node: DirectiveDefinitionNode) -> GraphQLDirective:
+        locations = [DirectiveLocation[node.value] for node in node.locations]
+
+        return GraphQLDirective(
+            name=node.name.value,
+            description=node.description.value if node.description else None,
+            locations=locations,
+            is_repeatable=node.repeatable,
+            args=build_argument_map(node.arguments),
+            ast_node=node,
+        )
+
+    def build_field_map(
+        nodes: Collection[
+            Union[
+                InterfaceTypeDefinitionNode,
+                InterfaceTypeExtensionNode,
+                ObjectTypeDefinitionNode,
+                ObjectTypeExtensionNode,
+            ]
+        ],
+    ) -> GraphQLFieldMap:
+        field_map: GraphQLFieldMap = {}
+        for node in nodes:
+            for field in node.fields or []:
+                # Note: While this could make assertions to get the correctly typed
+                # value, that would throw immediately while type system validation
+                # with validate_schema() will produce more actionable results.
+                field_map[field.name.value] = GraphQLField(
+                    type_=cast(GraphQLOutputType, get_wrapped_type(field.type)),
+                    description=field.description.value if field.description else None,
+                    args=build_argument_map(field.arguments),
+                    deprecation_reason=get_deprecation_reason(field),
+                    ast_node=field,
+                )
+        return field_map
+
+    def build_argument_map(
+        args: Optional[Collection[InputValueDefinitionNode]],
+    ) -> GraphQLArgumentMap:
+        arg_map: GraphQLArgumentMap = {}
+        for arg in args or []:
+            # Note: While this could make assertions to get the correctly typed
+            # value, that would throw immediately while type system validation
+            # with validate_schema() will produce more actionable results.
+            type_ = cast(GraphQLInputType, get_wrapped_type(arg.type))
+            arg_map[arg.name.value] = GraphQLArgument(
+                type_=type_,
+                description=arg.description.value if arg.description else None,
+                default_value=value_from_ast(arg.default_value, type_),
+                ast_node=arg,
+            )
+        return arg_map
+
+    def build_input_field_map(
+        nodes: Collection[
+            Union[InputObjectTypeDefinitionNode, InputObjectTypeExtensionNode]
+        ],
+    ) -> GraphQLInputFieldMap:
+        input_field_map: GraphQLInputFieldMap = {}
+        for node in nodes:
+            for field in node.fields or []:
+                # Note: While this could make assertions to get the correctly typed
+                # value, that would throw immediately while type system validation
+                # with validate_schema() will produce more actionable results.
+                type_ = cast(GraphQLInputType, get_wrapped_type(field.type))
+                input_field_map[field.name.value] = GraphQLInputField(
+                    type_=type_,
+                    description=field.description.value if field.description else None,
+                    default_value=value_from_ast(field.default_value, type_),
+                    ast_node=field,
+                )
+        return input_field_map
+
+    def build_enum_value_map(
+        nodes: Collection[Union[EnumTypeDefinitionNode, EnumTypeExtensionNode]]
+    ) -> GraphQLEnumValueMap:
+        enum_value_map: GraphQLEnumValueMap = {}
+        for node in nodes:
+            for value in node.values or []:
+                # Note: While this could make assertions to get the correctly typed
+                # value, that would throw immediately while type system validation
+                # with validate_schema() will produce more actionable results.
+                enum_value_map[value.name.value] = GraphQLEnumValue(
+                    description=value.description.value if value.description else None,
+                    deprecation_reason=get_deprecation_reason(value),
+                    ast_node=value,
+                )
+        return enum_value_map
+
+    def build_interfaces(
+        nodes: Collection[
+            Union[
+                InterfaceTypeDefinitionNode,
+                InterfaceTypeExtensionNode,
+                ObjectTypeDefinitionNode,
+                ObjectTypeExtensionNode,
+            ]
+        ],
+    ) -> List[GraphQLInterfaceType]:
+        interfaces: List[GraphQLInterfaceType] = []
+        for node in nodes:
+            for type_ in node.interfaces or []:
+                # Note: While this could make assertions to get the correctly typed
+                # value, that would throw immediately while type system validation
+                # with validate_schema() will produce more actionable results.
+                interfaces.append(cast(GraphQLInterfaceType, get_named_type(type_)))
+        return interfaces
+
+    def build_union_types(
+        nodes: Collection[Union[UnionTypeDefinitionNode, UnionTypeExtensionNode]],
+    ) -> List[GraphQLObjectType]:
+        types: List[GraphQLObjectType] = []
+        for node in nodes:
+            for type_ in node.types or []:
+                # Note: While this could make assertions to get the correctly typed
+                # value, that would throw immediately while type system validation
+                # with validate_schema() will produce more actionable results.
+                types.append(cast(GraphQLObjectType, get_named_type(type_)))
+        return types
+
+    def build_object_type(ast_node: ObjectTypeDefinitionNode) -> GraphQLObjectType:
+        extension_nodes = type_extensions_map[ast_node.name.value]
+        all_nodes: List[Union[ObjectTypeDefinitionNode, ObjectTypeExtensionNode]] = [
+            ast_node,
+            *extension_nodes,
+        ]
+        return GraphQLObjectType(
+            name=ast_node.name.value,
+            description=ast_node.description.value if ast_node.description else None,
+            interfaces=lambda: build_interfaces(all_nodes),
+            fields=lambda: build_field_map(all_nodes),
+            ast_node=ast_node,
+            extension_ast_nodes=extension_nodes,
+        )
+
+    def build_interface_type(
+        ast_node: InterfaceTypeDefinitionNode,
+    ) -> GraphQLInterfaceType:
+        extension_nodes = type_extensions_map[ast_node.name.value]
+        all_nodes: List[
+            Union[InterfaceTypeDefinitionNode, InterfaceTypeExtensionNode]
+        ] = [ast_node, *extension_nodes]
+        return GraphQLInterfaceType(
+            name=ast_node.name.value,
+            description=ast_node.description.value if ast_node.description else None,
+            interfaces=lambda: build_interfaces(all_nodes),
+            fields=lambda: build_field_map(all_nodes),
+            ast_node=ast_node,
+            extension_ast_nodes=extension_nodes,
+        )
+
+    def build_enum_type(ast_node: EnumTypeDefinitionNode) -> GraphQLEnumType:
+        extension_nodes = type_extensions_map[ast_node.name.value]
+        all_nodes: List[Union[EnumTypeDefinitionNode, EnumTypeExtensionNode]] = [
+            ast_node,
+            *extension_nodes,
+        ]
+        return GraphQLEnumType(
+            name=ast_node.name.value,
+            description=ast_node.description.value if ast_node.description else None,
+            values=build_enum_value_map(all_nodes),
+            ast_node=ast_node,
+            extension_ast_nodes=extension_nodes,
+        )
+
+    def build_union_type(ast_node: UnionTypeDefinitionNode) -> GraphQLUnionType:
+        extension_nodes = type_extensions_map[ast_node.name.value]
+        all_nodes: List[Union[UnionTypeDefinitionNode, UnionTypeExtensionNode]] = [
+            ast_node,
+            *extension_nodes,
+        ]
+        return GraphQLUnionType(
+            name=ast_node.name.value,
+            description=ast_node.description.value if ast_node.description else None,
+            types=lambda: build_union_types(all_nodes),
+            ast_node=ast_node,
+            extension_ast_nodes=extension_nodes,
+        )
+
+    def build_scalar_type(ast_node: ScalarTypeDefinitionNode) -> GraphQLScalarType:
+        extension_nodes = type_extensions_map[ast_node.name.value]
+        return GraphQLScalarType(
+            name=ast_node.name.value,
+            description=ast_node.description.value if ast_node.description else None,
+            specified_by_url=get_specified_by_url(ast_node),
+            ast_node=ast_node,
+            extension_ast_nodes=extension_nodes,
+        )
+
+    def build_input_object_type(
+        ast_node: InputObjectTypeDefinitionNode,
+    ) -> GraphQLInputObjectType:
+        extension_nodes = type_extensions_map[ast_node.name.value]
+        all_nodes: List[
+            Union[InputObjectTypeDefinitionNode, InputObjectTypeExtensionNode]
+        ] = [ast_node, *extension_nodes]
+        return GraphQLInputObjectType(
+            name=ast_node.name.value,
+            description=ast_node.description.value if ast_node.description else None,
+            fields=lambda: build_input_field_map(all_nodes),
+            ast_node=ast_node,
+            extension_ast_nodes=extension_nodes,
+        )
+
+    build_type_for_kind = cast(
+        Dict[str, Callable[[TypeDefinitionNode], GraphQLNamedType]],
+        {
+            "object_type_definition": build_object_type,
+            "interface_type_definition": build_interface_type,
+            "enum_type_definition": build_enum_type,
+            "union_type_definition": build_union_type,
+            "scalar_type_definition": build_scalar_type,
+            "input_object_type_definition": build_input_object_type,
+        },
+    )
+
+    def build_type(ast_node: TypeDefinitionNode) -> GraphQLNamedType:
+        try:
+            # object_type_definition_node is built with _build_object_type etc.
+            build_function = build_type_for_kind[ast_node.kind]
+        except KeyError:  # pragma: no cover
+            # Not reachable. All possible type definition nodes have been considered.
+            raise TypeError(  # pragma: no cover
+                f"Unexpected type definition node: {inspect(ast_node)}."
+            )
+        else:
+            return build_function(ast_node)
+
+    type_map: Dict[str, GraphQLNamedType] = {}
+    for existing_type in schema_kwargs["types"]:
+        type_map[existing_type.name] = extend_named_type(existing_type)
+    for type_node in type_defs:
+        name = type_node.name.value
+        type_map[name] = std_type_map.get(name) or build_type(type_node)
+
+    # Get the extended root operation types.
+    operation_types: Dict[OperationType, GraphQLNamedType] = {}
+    for operation_type in OperationType:
+        original_type = schema_kwargs[operation_type.value]
+        if original_type:
+            operation_types[operation_type] = replace_named_type(original_type)
+    # Then, incorporate schema definition and all schema extensions.
+    if schema_def:
+        operation_types.update(get_operation_types([schema_def]))
+    if schema_extensions:
+        operation_types.update(get_operation_types(schema_extensions))
+
+    # Then produce and return the kwargs for a Schema with these types.
+    get_operation = operation_types.get
+    return {
+        "query": get_operation(OperationType.QUERY),
+        "mutation": get_operation(OperationType.MUTATION),
+        "subscription": get_operation(OperationType.SUBSCRIPTION),
+        "types": type_map.values(),
+        "directives": [
+            replace_directive(directive) for directive in schema_kwargs["directives"]
+        ]
+        + [build_directive(directive) for directive in directive_defs],
+        "description": schema_def.description.value
+        if schema_def and schema_def.description
+        else None,
+        "extensions": None,
+        "ast_node": schema_def or schema_kwargs["ast_node"],
+        "extension_ast_nodes": schema_kwargs["extension_ast_nodes"] + schema_extensions,
+        "assume_valid": assume_valid,
+    }
+
+
+std_type_map: Dict[str, Union[GraphQLNamedType, GraphQLObjectType]] = {
+    **specified_scalar_types,
+    **introspection_types,
+}
+
+
+def get_deprecation_reason(
+    node: Union[EnumValueDefinitionNode, FieldDefinitionNode]
+) -> Optional[str]:
+    """Given a field or enum value node, get deprecation reason as string."""
+    from ..execution import get_directive_values
+
+    deprecated = get_directive_values(GraphQLDeprecatedDirective, node)
+    return deprecated["reason"] if deprecated else None
+
+
+def get_specified_by_url(
+    node: Union[ScalarTypeDefinitionNode, ScalarTypeExtensionNode]
+) -> Optional[str]:
+    """Given a scalar node, return the string value for the specifiedByUrl."""
+    from ..execution import get_directive_values
+
+    specified_by_url = get_directive_values(GraphQLSpecifiedByDirective, node)
+    return specified_by_url["url"] if specified_by_url else None
+
+
+def get_description(node: Node) -> Optional[str]:
+    """@deprecated: Given an ast node, returns its string description."""
+    try:
+        # noinspection PyUnresolvedReferences
+        return node.description.value  # type: ignore
+    except AttributeError:
+        return None
diff --git a/src/graphql/utilities/find_breaking_changes.py b/src/graphql/utilities/find_breaking_changes.py
new file mode 100644
index 0000000..19a649d
--- /dev/null
+++ b/src/graphql/utilities/find_breaking_changes.py
@@ -0,0 +1,631 @@
+from enum import Enum
+from operator import attrgetter
+from typing import Any, Dict, List, NamedTuple, Union, cast
+
+from ..language import print_ast, visit, ObjectValueNode, Visitor
+from ..pyutils import inspect, FrozenList, Undefined
+from ..type import (
+    GraphQLEnumType,
+    GraphQLField,
+    GraphQLList,
+    GraphQLNamedType,
+    GraphQLNonNull,
+    GraphQLInputType,
+    GraphQLInterfaceType,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLType,
+    GraphQLUnionType,
+    is_enum_type,
+    is_input_object_type,
+    is_interface_type,
+    is_list_type,
+    is_named_type,
+    is_non_null_type,
+    is_object_type,
+    is_required_argument,
+    is_required_input_field,
+    is_scalar_type,
+    is_specified_scalar_type,
+    is_union_type,
+)
+from .ast_from_value import ast_from_value
+
+__all__ = [
+    "BreakingChange",
+    "BreakingChangeType",
+    "DangerousChange",
+    "DangerousChangeType",
+    "find_breaking_changes",
+    "find_dangerous_changes",
+]
+
+
+class BreakingChangeType(Enum):
+    TYPE_REMOVED = 10
+    TYPE_CHANGED_KIND = 11
+    TYPE_REMOVED_FROM_UNION = 20
+    VALUE_REMOVED_FROM_ENUM = 21
+    REQUIRED_INPUT_FIELD_ADDED = 22
+    IMPLEMENTED_INTERFACE_REMOVED = 23
+    FIELD_REMOVED = 30
+    FIELD_CHANGED_KIND = 31
+    REQUIRED_ARG_ADDED = 40
+    ARG_REMOVED = 41
+    ARG_CHANGED_KIND = 42
+    DIRECTIVE_REMOVED = 50
+    DIRECTIVE_ARG_REMOVED = 51
+    REQUIRED_DIRECTIVE_ARG_ADDED = 52
+    DIRECTIVE_REPEATABLE_REMOVED = 53
+    DIRECTIVE_LOCATION_REMOVED = 54
+
+
+class DangerousChangeType(Enum):
+    VALUE_ADDED_TO_ENUM = 60
+    TYPE_ADDED_TO_UNION = 61
+    OPTIONAL_INPUT_FIELD_ADDED = 62
+    OPTIONAL_ARG_ADDED = 63
+    IMPLEMENTED_INTERFACE_ADDED = 64
+    ARG_DEFAULT_VALUE_CHANGE = 65
+
+
+class BreakingChange(NamedTuple):
+    type: BreakingChangeType
+    description: str
+
+
+class DangerousChange(NamedTuple):
+    type: DangerousChangeType
+    description: str
+
+
+Change = Union[BreakingChange, DangerousChange]
+
+
+def find_breaking_changes(
+    old_schema: GraphQLSchema, new_schema: GraphQLSchema
+) -> List[BreakingChange]:
+    """Find breaking changes.
+
+    Given two schemas, returns a list containing descriptions of all the types of
+    breaking changes covered by the other functions down below.
+    """
+    return [
+        change
+        for change in find_schema_changes(old_schema, new_schema)
+        if isinstance(change.type, BreakingChangeType)
+    ]
+
+
+def find_dangerous_changes(
+    old_schema: GraphQLSchema, new_schema: GraphQLSchema
+) -> List[DangerousChange]:
+    """Find dangerous changes.
+
+    Given two schemas, returns a list containing descriptions of all the types of
+    potentially dangerous changes covered by the other functions down below.
+    """
+    return [
+        change
+        for change in find_schema_changes(old_schema, new_schema)
+        if isinstance(change.type, DangerousChangeType)
+    ]
+
+
+def find_schema_changes(
+    old_schema: GraphQLSchema, new_schema: GraphQLSchema
+) -> List[Change]:
+    return find_type_changes(old_schema, new_schema) + find_directive_changes(
+        old_schema, new_schema
+    )
+
+
+def find_directive_changes(
+    old_schema: GraphQLSchema, new_schema: GraphQLSchema
+) -> List[Change]:
+    schema_changes: List[Change] = []
+
+    directives_diff = list_diff(old_schema.directives, new_schema.directives)
+
+    for directive in directives_diff.removed:
+        schema_changes.append(
+            BreakingChange(
+                BreakingChangeType.DIRECTIVE_REMOVED, f"{directive.name} was removed."
+            )
+        )
+
+    for (old_directive, new_directive) in directives_diff.persisted:
+        args_diff = dict_diff(old_directive.args, new_directive.args)
+
+        for arg_name, new_arg in args_diff.added.items():
+            if is_required_argument(new_arg):
+                schema_changes.append(
+                    BreakingChange(
+                        BreakingChangeType.REQUIRED_DIRECTIVE_ARG_ADDED,
+                        f"A required arg {arg_name} on directive"
+                        f" {old_directive.name} was added.",
+                    )
+                )
+
+        for arg_name in args_diff.removed:
+            schema_changes.append(
+                BreakingChange(
+                    BreakingChangeType.DIRECTIVE_ARG_REMOVED,
+                    f"{arg_name} was removed from {new_directive.name}.",
+                )
+            )
+
+        if old_directive.is_repeatable and not new_directive.is_repeatable:
+            schema_changes.append(
+                BreakingChange(
+                    BreakingChangeType.DIRECTIVE_REPEATABLE_REMOVED,
+                    f"Repeatable flag was removed from {old_directive.name}.",
+                )
+            )
+
+        for location in old_directive.locations:
+            if location not in new_directive.locations:
+                schema_changes.append(
+                    BreakingChange(
+                        BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
+                        f"{location.name} was removed from {new_directive.name}.",
+                    )
+                )
+
+    return schema_changes
+
+
+def find_type_changes(
+    old_schema: GraphQLSchema, new_schema: GraphQLSchema
+) -> List[Change]:
+    schema_changes: List[Change] = []
+    types_diff = dict_diff(old_schema.type_map, new_schema.type_map)
+
+    for type_name, old_type in types_diff.removed.items():
+        schema_changes.append(
+            BreakingChange(
+                BreakingChangeType.TYPE_REMOVED,
+                f"Standard scalar {type_name} was removed"
+                " because it is not referenced anymore."
+                if is_specified_scalar_type(old_type)
+                else f"{type_name} was removed.",
+            )
+        )
+
+    for type_name, (old_type, new_type) in types_diff.persisted.items():
+        if is_enum_type(old_type) and is_enum_type(new_type):
+            schema_changes.extend(find_enum_type_changes(old_type, new_type))
+        elif is_union_type(old_type) and is_union_type(new_type):
+            schema_changes.extend(find_union_type_changes(old_type, new_type))
+        elif is_input_object_type(old_type) and is_input_object_type(new_type):
+            schema_changes.extend(find_input_object_type_changes(old_type, new_type))
+        elif is_object_type(old_type) and is_object_type(new_type):
+            schema_changes.extend(find_field_changes(old_type, new_type))
+            schema_changes.extend(
+                find_implemented_interfaces_changes(old_type, new_type)
+            )
+        elif is_interface_type(old_type) and is_interface_type(new_type):
+            schema_changes.extend(find_field_changes(old_type, new_type))
+            schema_changes.extend(
+                find_implemented_interfaces_changes(old_type, new_type)
+            )
+        elif old_type.__class__ is not new_type.__class__:
+            schema_changes.append(
+                BreakingChange(
+                    BreakingChangeType.TYPE_CHANGED_KIND,
+                    f"{type_name} changed from {type_kind_name(old_type)}"
+                    f" to {type_kind_name(new_type)}.",
+                )
+            )
+
+    return schema_changes
+
+
+def find_input_object_type_changes(
+    old_type: Union[GraphQLObjectType, GraphQLInterfaceType],
+    new_type: Union[GraphQLObjectType, GraphQLInterfaceType],
+) -> List[Change]:
+    schema_changes: List[Change] = []
+    fields_diff = dict_diff(old_type.fields, new_type.fields)
+
+    for field_name, new_field in fields_diff.added.items():
+        if is_required_input_field(new_field):
+            schema_changes.append(
+                BreakingChange(
+                    BreakingChangeType.REQUIRED_INPUT_FIELD_ADDED,
+                    f"A required field {field_name} on"
+                    f" input type {old_type.name} was added.",
+                )
+            )
+        else:
+            schema_changes.append(
+                DangerousChange(
+                    DangerousChangeType.OPTIONAL_INPUT_FIELD_ADDED,
+                    f"An optional field {field_name} on"
+                    f" input type {old_type.name} was added.",
+                )
+            )
+
+    for field_name in fields_diff.removed:
+        schema_changes.append(
+            BreakingChange(
+                BreakingChangeType.FIELD_REMOVED,
+                f"{old_type.name}.{field_name} was removed.",
+            )
+        )
+
+    for field_name, (old_field, new_field) in fields_diff.persisted.items():
+        is_safe = is_change_safe_for_input_object_field_or_field_arg(
+            old_field.type, new_field.type
+        )
+        if not is_safe:
+            schema_changes.append(
+                BreakingChange(
+                    BreakingChangeType.FIELD_CHANGED_KIND,
+                    f"{old_type.name}.{field_name} changed type"
+                    f" from {old_field.type} to {new_field.type}.",
+                )
+            )
+
+    return schema_changes
+
+
+def find_union_type_changes(
+    old_type: GraphQLUnionType, new_type: GraphQLUnionType
+) -> List[Change]:
+    schema_changes: List[Change] = []
+    possible_types_diff = list_diff(old_type.types, new_type.types)
+
+    for possible_type in possible_types_diff.added:
+        schema_changes.append(
+            DangerousChange(
+                DangerousChangeType.TYPE_ADDED_TO_UNION,
+                f"{possible_type.name} was added" f" to union type {old_type.name}.",
+            )
+        )
+
+    for possible_type in possible_types_diff.removed:
+        schema_changes.append(
+            BreakingChange(
+                BreakingChangeType.TYPE_REMOVED_FROM_UNION,
+                f"{possible_type.name} was removed from union type {old_type.name}.",
+            )
+        )
+
+    return schema_changes
+
+
+def find_enum_type_changes(
+    old_type: GraphQLEnumType, new_type: GraphQLEnumType
+) -> List[Change]:
+    schema_changes: List[Change] = []
+    values_diff = dict_diff(old_type.values, new_type.values)
+
+    for value_name in values_diff.added:
+        schema_changes.append(
+            DangerousChange(
+                DangerousChangeType.VALUE_ADDED_TO_ENUM,
+                f"{value_name} was added to enum type {old_type.name}.",
+            )
+        )
+
+    for value_name in values_diff.removed:
+        schema_changes.append(
+            BreakingChange(
+                BreakingChangeType.VALUE_REMOVED_FROM_ENUM,
+                f"{value_name} was removed from enum type {old_type.name}.",
+            )
+        )
+
+    return schema_changes
+
+
+def find_implemented_interfaces_changes(
+    old_type: Union[GraphQLObjectType, GraphQLInterfaceType],
+    new_type: Union[GraphQLObjectType, GraphQLInterfaceType],
+) -> List[Change]:
+    schema_changes: List[Change] = []
+    interfaces_diff = list_diff(old_type.interfaces, new_type.interfaces)
+
+    for interface in interfaces_diff.added:
+        schema_changes.append(
+            DangerousChange(
+                DangerousChangeType.IMPLEMENTED_INTERFACE_ADDED,
+                f"{interface.name} added to interfaces implemented by {old_type.name}.",
+            )
+        )
+
+    for interface in interfaces_diff.removed:
+        schema_changes.append(
+            BreakingChange(
+                BreakingChangeType.IMPLEMENTED_INTERFACE_REMOVED,
+                f"{old_type.name} no longer implements interface {interface.name}.",
+            )
+        )
+
+    return schema_changes
+
+
+def find_field_changes(
+    old_type: Union[GraphQLObjectType, GraphQLInterfaceType],
+    new_type: Union[GraphQLObjectType, GraphQLInterfaceType],
+) -> List[Change]:
+    schema_changes: List[Change] = []
+    fields_diff = dict_diff(old_type.fields, new_type.fields)
+
+    for field_name in fields_diff.removed:
+        schema_changes.append(
+            BreakingChange(
+                BreakingChangeType.FIELD_REMOVED,
+                f"{old_type.name}.{field_name} was removed.",
+            )
+        )
+
+    for field_name, (old_field, new_field) in fields_diff.persisted.items():
+        schema_changes.extend(
+            find_arg_changes(old_type, field_name, old_field, new_field)
+        )
+        is_safe = is_change_safe_for_object_or_interface_field(
+            old_field.type, new_field.type
+        )
+        if not is_safe:
+            schema_changes.append(
+                BreakingChange(
+                    BreakingChangeType.FIELD_CHANGED_KIND,
+                    f"{old_type.name}.{field_name} changed type"
+                    f" from {old_field.type} to {new_field.type}.",
+                )
+            )
+
+    return schema_changes
+
+
+def find_arg_changes(
+    old_type: Union[GraphQLObjectType, GraphQLInterfaceType],
+    field_name: str,
+    old_field: GraphQLField,
+    new_field: GraphQLField,
+) -> List[Change]:
+    schema_changes: List[Change] = []
+    args_diff = dict_diff(old_field.args, new_field.args)
+
+    for arg_name in args_diff.removed:
+        schema_changes.append(
+            BreakingChange(
+                BreakingChangeType.ARG_REMOVED,
+                f"{old_type.name}.{field_name} arg" f" {arg_name} was removed.",
+            )
+        )
+
+    for arg_name, (old_arg, new_arg) in args_diff.persisted.items():
+        is_safe = is_change_safe_for_input_object_field_or_field_arg(
+            old_arg.type, new_arg.type
+        )
+        if not is_safe:
+            schema_changes.append(
+                BreakingChange(
+                    BreakingChangeType.ARG_CHANGED_KIND,
+                    f"{old_type.name}.{field_name} arg"
+                    f" {arg_name} has changed type from"
+                    f" {old_arg.type} to {new_arg.type}.",
+                )
+            )
+        elif old_arg.default_value is not Undefined:
+            if new_arg.default_value is Undefined:
+                schema_changes.append(
+                    DangerousChange(
+                        DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
+                        f"{old_type.name}.{field_name} arg"
+                        f" {arg_name} defaultValue was removed.",
+                    )
+                )
+            else:
+                # Since we are looking only for client's observable changes we should
+                # compare default values in the same representation as they are
+                # represented inside introspection.
+                old_value_str = stringify_value(old_arg.default_value, old_arg.type)
+                new_value_str = stringify_value(new_arg.default_value, new_arg.type)
+
+                if old_value_str != new_value_str:
+                    schema_changes.append(
+                        DangerousChange(
+                            DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
+                            f"{old_type.name}.{field_name} arg"
+                            f" {arg_name} has changed defaultValue"
+                            f" from {old_value_str} to {new_value_str}.",
+                        )
+                    )
+
+    for arg_name, new_arg in args_diff.added.items():
+        if is_required_argument(new_arg):
+            schema_changes.append(
+                BreakingChange(
+                    BreakingChangeType.REQUIRED_ARG_ADDED,
+                    f"A required arg {arg_name} on"
+                    f" {old_type.name}.{field_name} was added.",
+                )
+            )
+        else:
+            schema_changes.append(
+                DangerousChange(
+                    DangerousChangeType.OPTIONAL_ARG_ADDED,
+                    f"An optional arg {arg_name} on"
+                    f" {old_type.name}.{field_name} was added.",
+                )
+            )
+
+    return schema_changes
+
+
+def is_change_safe_for_object_or_interface_field(
+    old_type: GraphQLType, new_type: GraphQLType
+) -> bool:
+    if is_list_type(old_type):
+        return (
+            # if they're both lists, make sure underlying types are compatible
+            is_list_type(new_type)
+            and is_change_safe_for_object_or_interface_field(
+                cast(GraphQLList, old_type).of_type, cast(GraphQLList, new_type).of_type
+            )
+        ) or (
+            # moving from nullable to non-null of same underlying type is safe
+            is_non_null_type(new_type)
+            and is_change_safe_for_object_or_interface_field(
+                old_type, cast(GraphQLNonNull, new_type).of_type
+            )
+        )
+
+    if is_non_null_type(old_type):
+        # if they're both non-null, make sure underlying types are compatible
+        return is_non_null_type(
+            new_type
+        ) and is_change_safe_for_object_or_interface_field(
+            cast(GraphQLNonNull, old_type).of_type,
+            cast(GraphQLNonNull, new_type).of_type,
+        )
+
+    return (
+        # if they're both named types, see if their names are equivalent
+        is_named_type(new_type)
+        and cast(GraphQLNamedType, old_type).name
+        == cast(GraphQLNamedType, new_type).name
+    ) or (
+        # moving from nullable to non-null of same underlying type is safe
+        is_non_null_type(new_type)
+        and is_change_safe_for_object_or_interface_field(
+            old_type, cast(GraphQLNonNull, new_type).of_type
+        )
+    )
+
+
+def is_change_safe_for_input_object_field_or_field_arg(
+    old_type: GraphQLType, new_type: GraphQLType
+) -> bool:
+    if is_list_type(old_type):
+
+        return is_list_type(
+            # if they're both lists, make sure underlying types are compatible
+            new_type
+        ) and is_change_safe_for_input_object_field_or_field_arg(
+            cast(GraphQLList, old_type).of_type, cast(GraphQLList, new_type).of_type
+        )
+
+    if is_non_null_type(old_type):
+        return (
+            # if they're both non-null, make sure the underlying types are compatible
+            is_non_null_type(new_type)
+            and is_change_safe_for_input_object_field_or_field_arg(
+                cast(GraphQLNonNull, old_type).of_type,
+                cast(GraphQLNonNull, new_type).of_type,
+            )
+        ) or (
+            # moving from non-null to nullable of same underlying type is safe
+            not is_non_null_type(new_type)
+            and is_change_safe_for_input_object_field_or_field_arg(
+                cast(GraphQLNonNull, old_type).of_type, new_type
+            )
+        )
+
+    return (
+        # if they're both named types, see if their names are equivalent
+        is_named_type(new_type)
+        and cast(GraphQLNamedType, old_type).name
+        == cast(GraphQLNamedType, new_type).name
+    )
+
+
+def type_kind_name(type_: GraphQLNamedType) -> str:
+    if is_scalar_type(type_):
+        return "a Scalar type"
+    if is_object_type(type_):
+        return "an Object type"
+    if is_interface_type(type_):
+        return "an Interface type"
+    if is_union_type(type_):
+        return "a Union type"
+    if is_enum_type(type_):
+        return "an Enum type"
+    if is_input_object_type(type_):
+        return "an Input type"
+
+    # Not reachable. All possible output types have been considered.
+    raise TypeError(f"Unexpected type {inspect(type)}")
+
+
+def stringify_value(value: Any, type_: GraphQLInputType) -> str:
+    ast = ast_from_value(value, type_)
+    if ast is None:  # pragma: no cover
+        raise TypeError(f"Invalid value: {inspect(value)}")
+
+    class SortVisitor(Visitor):
+        @staticmethod
+        def enter_object_value(
+            object_value_node: ObjectValueNode, *_args: Any
+        ) -> ObjectValueNode:
+            object_value_node.fields = FrozenList(
+                sorted(object_value_node.fields, key=attrgetter("name.value"))
+            )
+            return object_value_node
+
+    sorted_ast = visit(ast, SortVisitor())
+
+    return print_ast(sorted_ast)
+
+
+class ListDiff(NamedTuple):
+    """Tuple with added, removed and persisted list items."""
+
+    added: List
+    removed: List
+    persisted: List
+
+
+def list_diff(old_list: List, new_list: List) -> ListDiff:
+    """Get differences between two lists of named items."""
+    added = []
+    persisted = []
+    removed = []
+
+    old_set = {item.name for item in old_list}
+    new_map = {item.name: item for item in new_list}
+
+    for old_item in old_list:
+        new_item = new_map.get(old_item.name)
+        if new_item:
+            persisted.append([old_item, new_item])
+        else:
+            removed.append(old_item)
+
+    for new_item in new_list:
+        if new_item.name not in old_set:
+            added.append(new_item)
+
+    return ListDiff(added, removed, persisted)
+
+
+class DictDiff(NamedTuple):
+    """Tuple with added, removed and persisted dict entries."""
+
+    added: Dict
+    removed: Dict
+    persisted: Dict
+
+
+def dict_diff(old_dict: Dict, new_dict: Dict) -> DictDiff:
+    """Get differences between two dicts."""
+    added = {}
+    removed = {}
+    persisted = {}
+
+    for old_name, old_item in old_dict.items():
+        new_item = new_dict.get(old_name)
+        if new_item:
+            persisted[old_name] = [old_item, new_item]
+        else:
+            removed[old_name] = old_item
+
+    for new_name, new_item in new_dict.items():
+        if new_name not in old_dict:
+            added[new_name] = new_item
+
+    return DictDiff(added, removed, persisted)
diff --git a/src/graphql/utilities/find_deprecated_usages.py b/src/graphql/utilities/find_deprecated_usages.py
new file mode 100644
index 0000000..2f76a6d
--- /dev/null
+++ b/src/graphql/utilities/find_deprecated_usages.py
@@ -0,0 +1,56 @@
+from typing import Any, List
+
+from ..error import GraphQLError
+from ..language import DocumentNode, EnumValueNode, FieldNode, Visitor, visit
+from ..type import GraphQLSchema, get_named_type
+from .type_info import TypeInfo, TypeInfoVisitor
+
+
+__all__ = ["find_deprecated_usages"]
+
+
+def find_deprecated_usages(
+    schema: GraphQLSchema, ast: DocumentNode
+) -> List[GraphQLError]:
+    """Get a list of GraphQLError instances describing each deprecated use."""
+
+    type_info = TypeInfo(schema)
+    visitor = FindDeprecatedUsages(type_info)
+    visit(ast, TypeInfoVisitor(type_info, visitor))
+    return visitor.errors
+
+
+class FindDeprecatedUsages(Visitor):
+    """A validation rule which reports deprecated usages."""
+
+    type_info: TypeInfo
+    errors: List[GraphQLError]
+
+    def __init__(self, type_info: TypeInfo):
+        super().__init__()
+        self.type_info = type_info
+        self.errors = []
+
+    def enter_field(self, node: FieldNode, *_args: Any) -> None:
+        parent_type = self.type_info.get_parent_type()
+        field_def = self.type_info.get_field_def()
+        if parent_type and field_def and field_def.deprecation_reason is not None:
+            self.errors.append(
+                GraphQLError(
+                    f"The field '{parent_type.name}.{node.name.value}'"
+                    " is deprecated. " + field_def.deprecation_reason,
+                    node,
+                )
+            )
+
+    def enter_enum_value(self, node: EnumValueNode, *_args: Any) -> None:
+        type_ = get_named_type(self.type_info.get_input_type())
+        enum_val = self.type_info.get_enum_value()
+        if type_ and enum_val and enum_val.deprecation_reason is not None:
+            self.errors.append(
+                GraphQLError(
+                    f"The enum value '{type_.name}.{node.value}'"
+                    " is deprecated. " + enum_val.deprecation_reason,
+                    node,
+                )
+            )
diff --git a/src/graphql/utilities/get_introspection_query.py b/src/graphql/utilities/get_introspection_query.py
new file mode 100644
index 0000000..c7299c6
--- /dev/null
+++ b/src/graphql/utilities/get_introspection_query.py
@@ -0,0 +1,119 @@
+from textwrap import dedent
+
+__all__ = ["get_introspection_query"]
+
+
+def get_introspection_query(
+    descriptions: bool = True,
+    specified_by_url: bool = False,
+    directive_is_repeatable: bool = False,
+    schema_description: bool = False,
+) -> str:
+    """Get a query for introspection.
+
+    Optionally, you can exclude descriptions, include specification URLs,
+    include repeatability of directives, and specify whether to include
+    the schema description as well.
+    """
+    maybe_description = "description" if descriptions else ""
+    maybe_specified_by_url = "specifiedByUrl" if specified_by_url else ""
+    maybe_directive_is_repeatable = "isRepeatable" if directive_is_repeatable else ""
+    maybe_schema_description = maybe_description if schema_description else ""
+    return dedent(
+        f"""
+        query IntrospectionQuery {{
+          __schema {{
+            {maybe_schema_description}
+            queryType {{ name }}
+            mutationType {{ name }}
+            subscriptionType {{ name }}
+            types {{
+              ...FullType
+            }}
+            directives {{
+              name
+              {maybe_description}
+              {maybe_directive_is_repeatable}
+              locations
+              args {{
+                ...InputValue
+              }}
+            }}
+          }}
+        }}
+
+        fragment FullType on __Type {{
+          kind
+          name
+          {maybe_description}
+          {maybe_specified_by_url}
+          fields(includeDeprecated: true) {{
+            name
+            {maybe_description}
+            args {{
+              ...InputValue
+            }}
+            type {{
+              ...TypeRef
+            }}
+            isDeprecated
+            deprecationReason
+          }}
+          inputFields {{
+            ...InputValue
+          }}
+          interfaces {{
+            ...TypeRef
+          }}
+          enumValues(includeDeprecated: true) {{
+            name
+            {maybe_description}
+            isDeprecated
+            deprecationReason
+          }}
+          possibleTypes {{
+            ...TypeRef
+          }}
+        }}
+
+        fragment InputValue on __InputValue {{
+          name
+          {maybe_description}
+          type {{ ...TypeRef }}
+          defaultValue
+        }}
+
+        fragment TypeRef on __Type {{
+          kind
+          name
+          ofType {{
+            kind
+            name
+            ofType {{
+              kind
+              name
+              ofType {{
+                kind
+                name
+                ofType {{
+                  kind
+                  name
+                  ofType {{
+                    kind
+                    name
+                    ofType {{
+                      kind
+                      name
+                      ofType {{
+                        kind
+                        name
+                      }}
+                    }}
+                  }}
+                }}
+              }}
+            }}
+          }}
+        }}
+        """
+    )
diff --git a/src/graphql/utilities/get_operation_ast.py b/src/graphql/utilities/get_operation_ast.py
new file mode 100644
index 0000000..b7d7931
--- /dev/null
+++ b/src/graphql/utilities/get_operation_ast.py
@@ -0,0 +1,29 @@
+from typing import Optional
+
+from ..language import DocumentNode, OperationDefinitionNode
+
+__all__ = ["get_operation_ast"]
+
+
+def get_operation_ast(
+    document_ast: DocumentNode, operation_name: Optional[str] = None
+) -> Optional[OperationDefinitionNode]:
+    """Get operation AST node.
+
+    Returns an operation AST given a document AST and optionally an operation
+    name. If a name is not provided, an operation is only returned if only one
+    is provided in the document.
+    """
+    operation = None
+    for definition in document_ast.definitions:
+        if isinstance(definition, OperationDefinitionNode):
+            if operation_name is None:
+                # If no operation name was provided, only return an Operation if there
+                # is one defined in the document.
+                # Upon encountering the second, return None.
+                if operation:
+                    return None
+                operation = definition
+            elif definition.name and definition.name.value == operation_name:
+                return definition
+    return operation
diff --git a/src/graphql/utilities/get_operation_root_type.py b/src/graphql/utilities/get_operation_root_type.py
new file mode 100644
index 0000000..305e721
--- /dev/null
+++ b/src/graphql/utilities/get_operation_root_type.py
@@ -0,0 +1,42 @@
+from typing import Union
+
+from ..error import GraphQLError
+from ..language import (
+    OperationType,
+    OperationDefinitionNode,
+    OperationTypeDefinitionNode,
+)
+from ..type import GraphQLObjectType, GraphQLSchema
+
+__all__ = ["get_operation_root_type"]
+
+
+def get_operation_root_type(
+    schema: GraphQLSchema,
+    operation: Union[OperationDefinitionNode, OperationTypeDefinitionNode],
+) -> GraphQLObjectType:
+    """Extract the root type of the operation from the schema."""
+    operation_type = operation.operation
+    if operation_type == OperationType.QUERY:
+        query_type = schema.query_type
+        if not query_type:
+            raise GraphQLError(
+                "Schema does not define the required query root type.", operation
+            )
+        return query_type
+
+    if operation_type == OperationType.MUTATION:
+        mutation_type = schema.mutation_type
+        if not mutation_type:
+            raise GraphQLError("Schema is not configured for mutations.", operation)
+        return mutation_type
+
+    if operation_type == OperationType.SUBSCRIPTION:
+        subscription_type = schema.subscription_type
+        if not subscription_type:
+            raise GraphQLError("Schema is not configured for subscriptions.", operation)
+        return subscription_type
+
+    raise GraphQLError(
+        "Can only have query, mutation and subscription operations.", operation
+    )
diff --git a/src/graphql/utilities/introspection_from_schema.py b/src/graphql/utilities/introspection_from_schema.py
new file mode 100644
index 0000000..6418161
--- /dev/null
+++ b/src/graphql/utilities/introspection_from_schema.py
@@ -0,0 +1,44 @@
+from typing import Any, Dict
+
+from ..error import GraphQLError
+from ..language import parse
+from ..type import GraphQLSchema
+from .get_introspection_query import get_introspection_query
+
+__all__ = ["introspection_from_schema"]
+
+
+IntrospectionSchema = Dict[str, Any]
+
+
+def introspection_from_schema(
+    schema: GraphQLSchema,
+    descriptions: bool = True,
+    specified_by_url: bool = False,
+    directive_is_repeatable: bool = True,
+    schema_description: bool = True,
+) -> IntrospectionSchema:
+    """Build an IntrospectionQuery from a GraphQLSchema
+
+    IntrospectionQuery is useful for utilities that care about type and field
+    relationships, but do not need to traverse through those relationships.
+
+    This is the inverse of build_client_schema. The primary use case is outside of the
+    server context, for instance when doing schema comparisons.
+    """
+    document = parse(
+        get_introspection_query(
+            descriptions, specified_by_url, directive_is_repeatable, schema_description
+        )
+    )
+
+    from ..execution.execute import execute, ExecutionResult
+
+    result = execute(schema, document)
+    if not isinstance(result, ExecutionResult):  # pragma: no cover
+        raise RuntimeError("Introspection cannot be executed")
+    if result.errors:  # pragma: no cover
+        raise result.errors[0]
+    if not result.data:  # pragma: no cover
+        raise GraphQLError("Introspection did not return a result")
+    return result.data
diff --git a/src/graphql/utilities/lexicographic_sort_schema.py b/src/graphql/utilities/lexicographic_sort_schema.py
new file mode 100644
index 0000000..4cb49f1
--- /dev/null
+++ b/src/graphql/utilities/lexicographic_sort_schema.py
@@ -0,0 +1,180 @@
+from typing import Dict, List, Optional, Tuple, Union, cast
+
+from ..language import DirectiveLocation
+from ..pyutils import inspect, FrozenList
+from ..type import (
+    GraphQLArgument,
+    GraphQLDirective,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInputType,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNamedType,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLUnionType,
+    is_enum_type,
+    is_input_object_type,
+    is_interface_type,
+    is_introspection_type,
+    is_list_type,
+    is_non_null_type,
+    is_object_type,
+    is_scalar_type,
+    is_union_type,
+)
+
+__all__ = ["lexicographic_sort_schema"]
+
+
+def lexicographic_sort_schema(schema: GraphQLSchema) -> GraphQLSchema:
+    """Sort GraphQLSchema.
+
+    This function returns a sorted copy of the given GraphQLSchema.
+    """
+
+    def replace_type(
+        type_: Union[GraphQLList, GraphQLNonNull, GraphQLNamedType]
+    ) -> Union[GraphQLList, GraphQLNonNull, GraphQLNamedType]:
+        if is_list_type(type_):
+            return GraphQLList(replace_type(cast(GraphQLList, type_).of_type))
+        if is_non_null_type(type_):
+            return GraphQLNonNull(replace_type(cast(GraphQLNonNull, type_).of_type))
+        return replace_named_type(cast(GraphQLNamedType, type_))
+
+    def replace_named_type(type_: GraphQLNamedType) -> GraphQLNamedType:
+        return type_map[type_.name]
+
+    def replace_maybe_type(
+        maybe_type: Optional[GraphQLNamedType],
+    ) -> Optional[GraphQLNamedType]:
+        return maybe_type and replace_named_type(maybe_type)
+
+    def sort_directive(directive: GraphQLDirective) -> GraphQLDirective:
+        kwargs = directive.to_kwargs()
+        kwargs.update(
+            locations=sorted(directive.locations, key=sort_by_name_key),
+            args=sort_args(directive.args),
+        )
+        return GraphQLDirective(**kwargs)
+
+    def sort_args(args_map: Dict[str, GraphQLArgument]) -> Dict[str, GraphQLArgument]:
+        args = {}
+        for name, arg in sorted(args_map.items()):
+            kwargs = arg.to_kwargs()
+            kwargs.update(type_=replace_type(cast(GraphQLNamedType, arg.type)))
+            args[name] = GraphQLArgument(**kwargs)
+        return args
+
+    def sort_fields(fields_map: Dict[str, GraphQLField]) -> Dict[str, GraphQLField]:
+        fields = {}
+        for name, field in sorted(fields_map.items()):
+            kwargs = field.to_kwargs()
+            kwargs.update(
+                type_=replace_type(cast(GraphQLNamedType, field.type)),
+                args=sort_args(field.args),
+            )
+            fields[name] = GraphQLField(**kwargs)
+        return fields
+
+    def sort_input_fields(
+        fields_map: Dict[str, GraphQLInputField]
+    ) -> Dict[str, GraphQLInputField]:
+        return {
+            name: GraphQLInputField(
+                cast(
+                    GraphQLInputType, replace_type(cast(GraphQLNamedType, field.type))
+                ),
+                description=field.description,
+                default_value=field.default_value,
+                ast_node=field.ast_node,
+            )
+            for name, field in sorted(fields_map.items())
+        }
+
+    def sort_types(arr: FrozenList[GraphQLNamedType]) -> List[GraphQLNamedType]:
+        return [
+            replace_named_type(type_) for type_ in sorted(arr, key=sort_by_name_key)
+        ]
+
+    def sort_named_type(type_: GraphQLNamedType) -> GraphQLNamedType:
+        if is_scalar_type(type_) or is_introspection_type(type_):
+            return type_
+        if is_object_type(type_):
+            kwargs = type_.to_kwargs()
+            type_ = cast(GraphQLObjectType, type_)
+            kwargs.update(
+                interfaces=lambda: sort_types(type_.interfaces),
+                fields=lambda: sort_fields(type_.fields),
+            )
+            return GraphQLObjectType(**kwargs)
+        if is_interface_type(type_):
+            kwargs = type_.to_kwargs()
+            type_ = cast(GraphQLInterfaceType, type_)
+            kwargs.update(
+                interfaces=lambda: sort_types(type_.interfaces),
+                fields=lambda: sort_fields(type_.fields),
+            )
+            return GraphQLInterfaceType(**kwargs)
+        if is_union_type(type_):
+            kwargs = type_.to_kwargs()
+            type_ = cast(GraphQLUnionType, type_)
+            kwargs.update(types=lambda: sort_types(type_.types))
+            return GraphQLUnionType(**kwargs)
+        if is_enum_type(type_):
+            kwargs = type_.to_kwargs()
+            type_ = cast(GraphQLEnumType, type_)
+            kwargs.update(
+                values={
+                    name: GraphQLEnumValue(
+                        val.value,
+                        description=val.description,
+                        deprecation_reason=val.deprecation_reason,
+                        ast_node=val.ast_node,
+                    )
+                    for name, val in sorted(type_.values.items())
+                }
+            )
+            return GraphQLEnumType(**kwargs)
+        if is_input_object_type(type_):
+            kwargs = type_.to_kwargs()
+            type_ = cast(GraphQLInputObjectType, type_)
+            kwargs.update(fields=lambda: sort_input_fields(type_.fields))
+            return GraphQLInputObjectType(**kwargs)
+
+        # Not reachable. All possible types have been considered.
+        raise TypeError(f"Unexpected type: {inspect(type_)}.")
+
+    type_map: Dict[str, GraphQLNamedType] = {
+        type_.name: sort_named_type(type_)
+        for type_ in sorted(schema.type_map.values(), key=sort_by_name_key)
+    }
+
+    return GraphQLSchema(
+        types=type_map.values(),
+        directives=[
+            sort_directive(directive)
+            for directive in sorted(schema.directives, key=sort_by_name_key)
+        ],
+        query=cast(Optional[GraphQLObjectType], replace_maybe_type(schema.query_type)),
+        mutation=cast(
+            Optional[GraphQLObjectType], replace_maybe_type(schema.mutation_type)
+        ),
+        subscription=cast(
+            Optional[GraphQLObjectType], replace_maybe_type(schema.subscription_type)
+        ),
+        ast_node=schema.ast_node,
+    )
+
+
+def sort_by_name_key(
+    type_: Union[GraphQLNamedType, GraphQLDirective, DirectiveLocation]
+) -> Tuple[bool, str]:
+    name = type_.name
+    # GraphQL.JS sorts '_' first using localeCompare
+    return not name.startswith("_"), name
diff --git a/src/graphql/utilities/print_schema.py b/src/graphql/utilities/print_schema.py
new file mode 100644
index 0000000..8b2471c
--- /dev/null
+++ b/src/graphql/utilities/print_schema.py
@@ -0,0 +1,307 @@
+from itertools import chain
+from typing import Any, Callable, Dict, List, Optional, Union, cast
+
+from ..language import print_ast
+from ..language.block_string import print_block_string
+from ..pyutils import inspect
+from ..type import (
+    DEFAULT_DEPRECATION_REASON,
+    GraphQLArgument,
+    GraphQLDirective,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputObjectType,
+    GraphQLInputType,
+    GraphQLInterfaceType,
+    GraphQLNamedType,
+    GraphQLObjectType,
+    GraphQLScalarType,
+    GraphQLSchema,
+    GraphQLString,
+    GraphQLUnionType,
+    is_enum_type,
+    is_input_object_type,
+    is_interface_type,
+    is_introspection_type,
+    is_object_type,
+    is_scalar_type,
+    is_specified_directive,
+    is_specified_scalar_type,
+    is_union_type,
+)
+from .ast_from_value import ast_from_value
+
+__all__ = ["print_schema", "print_introspection_schema", "print_type", "print_value"]
+
+
+def print_schema(schema: GraphQLSchema) -> str:
+    return print_filtered_schema(
+        schema, lambda n: not is_specified_directive(n), is_defined_type
+    )
+
+
+def print_introspection_schema(schema: GraphQLSchema) -> str:
+    return print_filtered_schema(schema, is_specified_directive, is_introspection_type)
+
+
+def is_defined_type(type_: GraphQLNamedType) -> bool:
+    return not is_specified_scalar_type(type_) and not is_introspection_type(type_)
+
+
+def print_filtered_schema(
+    schema: GraphQLSchema,
+    directive_filter: Callable[[GraphQLDirective], bool],
+    type_filter: Callable[[GraphQLNamedType], bool],
+) -> str:
+    directives = filter(directive_filter, schema.directives)
+    types = filter(type_filter, schema.type_map.values())
+
+    return (
+        "\n\n".join(
+            chain(
+                filter(None, [print_schema_definition(schema)]),
+                (print_directive(directive) for directive in directives),
+                (print_type(type_) for type_ in types),
+            )
+        )
+        + "\n"
+    )
+
+
+def print_schema_definition(schema: GraphQLSchema) -> Optional[str]:
+    if schema.description is None and is_schema_of_common_names(schema):
+        return None
+
+    operation_types = []
+
+    query_type = schema.query_type
+    if query_type:
+        operation_types.append(f"  query: {query_type.name}")
+
+    mutation_type = schema.mutation_type
+    if mutation_type:
+        operation_types.append(f"  mutation: {mutation_type.name}")
+
+    subscription_type = schema.subscription_type
+    if subscription_type:
+        operation_types.append(f"  subscription: {subscription_type.name}")
+
+    return print_description(schema) + "schema {\n" + "\n".join(operation_types) + "\n}"
+
+
+def is_schema_of_common_names(schema: GraphQLSchema) -> bool:
+    """Check whether this schema uses the common naming convention.
+
+    GraphQL schema define root types for each type of operation. These types are the
+    same as any other type and can be named in any manner, however there is a common
+    naming convention:
+
+    schema {
+      query: Query
+      mutation: Mutation
+    }
+
+    When using this naming convention, the schema description can be omitted.
+    """
+    query_type = schema.query_type
+    if query_type and query_type.name != "Query":
+        return False
+
+    mutation_type = schema.mutation_type
+    if mutation_type and mutation_type.name != "Mutation":
+        return False
+
+    subscription_type = schema.subscription_type
+    if subscription_type and subscription_type.name != "Subscription":
+        return False
+
+    return True
+
+
+def print_type(type_: GraphQLNamedType) -> str:
+    if is_scalar_type(type_):
+        type_ = cast(GraphQLScalarType, type_)
+        return print_scalar(type_)
+    if is_object_type(type_):
+        type_ = cast(GraphQLObjectType, type_)
+        return print_object(type_)
+    if is_interface_type(type_):
+        type_ = cast(GraphQLInterfaceType, type_)
+        return print_interface(type_)
+    if is_union_type(type_):
+        type_ = cast(GraphQLUnionType, type_)
+        return print_union(type_)
+    if is_enum_type(type_):
+        type_ = cast(GraphQLEnumType, type_)
+        return print_enum(type_)
+    if is_input_object_type(type_):
+        type_ = cast(GraphQLInputObjectType, type_)
+        return print_input_object(type_)
+
+    # Not reachable. All possible types have been considered.
+    raise TypeError(f"Unexpected type: {inspect(type_)}.")
+
+
+def print_scalar(type_: GraphQLScalarType) -> str:
+    return (
+        print_description(type_)
+        + f"scalar {type_.name}"
+        + print_specified_by_url(type_)
+    )
+
+
+def print_implemented_interfaces(
+    type_: Union[GraphQLObjectType, GraphQLInterfaceType]
+) -> str:
+    interfaces = type_.interfaces
+    return " implements " + " & ".join(i.name for i in interfaces) if interfaces else ""
+
+
+def print_object(type_: GraphQLObjectType) -> str:
+    return (
+        print_description(type_)
+        + f"type {type_.name}"
+        + print_implemented_interfaces(type_)
+        + print_fields(type_)
+    )
+
+
+def print_interface(type_: GraphQLInterfaceType) -> str:
+    return (
+        print_description(type_)
+        + f"interface {type_.name}"
+        + print_implemented_interfaces(type_)
+        + print_fields(type_)
+    )
+
+
+def print_union(type_: GraphQLUnionType) -> str:
+    types = type_.types
+    possible_types = " = " + " | ".join(t.name for t in types) if types else ""
+    return print_description(type_) + f"union {type_.name}" + possible_types
+
+
+def print_enum(type_: GraphQLEnumType) -> str:
+    values = [
+        print_description(value, "  ", not i) + f"  {name}" + print_deprecated(value)
+        for i, (name, value) in enumerate(type_.values.items())
+    ]
+    return print_description(type_) + f"enum {type_.name}" + print_block(values)
+
+
+def print_input_object(type_: GraphQLInputObjectType) -> str:
+    fields = [
+        print_description(field, "  ", not i) + "  " + print_input_value(name, field)
+        for i, (name, field) in enumerate(type_.fields.items())
+    ]
+    return print_description(type_) + f"input {type_.name}" + print_block(fields)
+
+
+def print_fields(type_: Union[GraphQLObjectType, GraphQLInterfaceType]) -> str:
+    fields = [
+        print_description(field, "  ", not i)
+        + f"  {name}"
+        + print_args(field.args, "  ")
+        + f": {field.type}"
+        + print_deprecated(field)
+        for i, (name, field) in enumerate(type_.fields.items())
+    ]
+    return print_block(fields)
+
+
+def print_block(items: List[str]) -> str:
+    return " {\n" + "\n".join(items) + "\n}" if items else ""
+
+
+def print_args(args: Dict[str, GraphQLArgument], indentation: str = "") -> str:
+    if not args:
+        return ""
+
+    # If every arg does not have a description, print them on one line.
+    if not any(arg.description for arg in args.values()):
+        return (
+            "("
+            + ", ".join(print_input_value(name, arg) for name, arg in args.items())
+            + ")"
+        )
+
+    return (
+        "(\n"
+        + "\n".join(
+            print_description(arg, f"  {indentation}", not i)
+            + f"  {indentation}"
+            + print_input_value(name, arg)
+            for i, (name, arg) in enumerate(args.items())
+        )
+        + f"\n{indentation})"
+    )
+
+
+def print_input_value(name: str, arg: GraphQLArgument) -> str:
+    default_ast = ast_from_value(arg.default_value, arg.type)
+    arg_decl = f"{name}: {arg.type}"
+    if default_ast:
+        arg_decl += f" = {print_ast(default_ast)}"
+    return arg_decl
+
+
+def print_directive(directive: GraphQLDirective) -> str:
+    return (
+        print_description(directive)
+        + f"directive @{directive.name}"
+        + print_args(directive.args)
+        + (" repeatable" if directive.is_repeatable else "")
+        + " on "
+        + " | ".join(location.name for location in directive.locations)
+    )
+
+
+def print_deprecated(field_or_enum_value: Union[GraphQLField, GraphQLEnumValue]) -> str:
+    if not field_or_enum_value.is_deprecated:
+        return ""
+    reason = field_or_enum_value.deprecation_reason
+    reason_ast = ast_from_value(reason, GraphQLString)
+    if not reason_ast or reason == DEFAULT_DEPRECATION_REASON:
+        return " @deprecated"
+    return f" @deprecated(reason: {print_ast(reason_ast)})"
+
+
+def print_specified_by_url(scalar: GraphQLScalarType) -> str:
+    if scalar.specified_by_url is None:
+        return ""
+    url = scalar.specified_by_url
+    url_ast = ast_from_value(url, GraphQLString)
+    if not url_ast:  # pragma: no cover
+        raise TypeError(
+            "Unexpected null value returned from `ast_from_value` for specifiedByUrl."
+        )
+    return f" @specifiedBy(url: {print_ast(url_ast)})"
+
+
+def print_description(
+    def_: Union[
+        GraphQLArgument,
+        GraphQLDirective,
+        GraphQLEnumValue,
+        GraphQLNamedType,
+        GraphQLSchema,
+    ],
+    indentation: str = "",
+    first_in_block: bool = True,
+) -> str:
+    description = def_.description
+    if description is None:
+        return ""
+
+    prefer_multiple_lines = len(description) > 70
+    block_string = print_block_string(description, "", prefer_multiple_lines)
+
+    prefix = "\n" + indentation if indentation and not first_in_block else indentation
+
+    return prefix + block_string.replace("\n", "\n" + indentation) + "\n"
+
+
+def print_value(value: Any, type_: GraphQLInputType) -> str:
+    """@deprecated: Convenience function for printing a Python value"""
+    return print_ast(ast_from_value(value, type_))  # type: ignore
diff --git a/src/graphql/utilities/separate_operations.py b/src/graphql/utilities/separate_operations.py
new file mode 100644
index 0000000..713365a
--- /dev/null
+++ b/src/graphql/utilities/separate_operations.py
@@ -0,0 +1,99 @@
+from collections import defaultdict
+from typing import Any, DefaultDict, Dict, List, Set
+
+from ..language import (
+    DocumentNode,
+    FragmentDefinitionNode,
+    FragmentSpreadNode,
+    OperationDefinitionNode,
+    Visitor,
+    visit,
+)
+
+__all__ = ["separate_operations"]
+
+
+DepGraph = DefaultDict[str, Set[str]]
+
+
+def separate_operations(document_ast: DocumentNode) -> Dict[str, DocumentNode]:
+    """Separate operations in a given AST document.
+
+    This function accepts a single AST document which may contain many operations and
+    fragments and returns a collection of AST documents each of which contains a single
+    operation as well the fragment definitions it refers to.
+    """
+    # Populate metadata and build a dependency graph.
+    visitor = SeparateOperations()
+    visit(document_ast, visitor)
+    operations = visitor.operations
+    dep_graph = visitor.dep_graph
+
+    # For each operation, produce a new synthesized AST which includes only what is
+    # necessary for completing that operation.
+    separated_document_asts = {}
+    for operation in operations:
+        operation_name = op_name(operation)
+        dependencies: Set[str] = set()
+        collect_transitive_dependencies(dependencies, dep_graph, operation_name)
+
+        # The list of definition nodes to be included for this operation, sorted
+        # to retain the same order as the original document.
+        separated_document_asts[operation_name] = DocumentNode(
+            definitions=[
+                node
+                for node in document_ast.definitions
+                if node is operation
+                or (
+                    isinstance(node, FragmentDefinitionNode)
+                    and node.name.value in dependencies
+                )
+            ]
+        )
+
+    return separated_document_asts
+
+
+class SeparateOperations(Visitor):
+    operations: List[OperationDefinitionNode]
+    dep_graph: DepGraph
+    from_name: str
+
+    def __init__(self) -> None:
+        super().__init__()
+        self.operations = []
+        self.dep_graph = defaultdict(set)
+
+    def enter_operation_definition(
+        self, node: OperationDefinitionNode, *_args: Any
+    ) -> None:
+        self.from_name = op_name(node)
+        self.operations.append(node)
+
+    def enter_fragment_definition(
+        self, node: FragmentDefinitionNode, *_args: Any
+    ) -> None:
+        self.from_name = node.name.value
+
+    def enter_fragment_spread(self, node: FragmentSpreadNode, *_args: Any) -> None:
+        self.dep_graph[self.from_name].add(node.name.value)
+
+
+def op_name(operation: OperationDefinitionNode) -> str:
+    """Provide the empty string for anonymous operations."""
+    return operation.name.value if operation.name else ""
+
+
+def collect_transitive_dependencies(
+    collected: Set[str], dep_graph: DepGraph, from_name: str
+) -> None:
+    """Collect transitive dependencies.
+
+    From a dependency graph, collects a list of transitive dependencies by recursing
+    through a dependency graph.
+    """
+    immediate_deps = dep_graph[from_name]
+    for to_name in immediate_deps:
+        if to_name not in collected:
+            collected.add(to_name)
+            collect_transitive_dependencies(collected, dep_graph, to_name)
diff --git a/src/graphql/utilities/strip_ignored_characters.py b/src/graphql/utilities/strip_ignored_characters.py
new file mode 100644
index 0000000..2435ffc
--- /dev/null
+++ b/src/graphql/utilities/strip_ignored_characters.py
@@ -0,0 +1,115 @@
+from typing import Union
+
+from ..language import Lexer, Source, TokenKind
+from ..language.block_string import (
+    dedent_block_string_value,
+    get_block_string_indentation,
+)
+from ..language.lexer import is_punctuator_token_kind
+from ..pyutils import inspect
+
+
+def strip_ignored_characters(source: Union[str, Source]) -> str:
+    """Strip characters that are ignored anyway.
+
+    Strips characters that are not significant to the validity or execution
+    of a GraphQL document:
+
+        - UnicodeBOM
+        - WhiteSpace
+        - LineTerminator
+        - Comment
+        - Comma
+        - BlockString indentation
+
+    Note: It is required to have a delimiter character between neighboring
+    non-punctuator tokes and this function always uses single space as delimiter.
+
+    It is guaranteed that both input and output documents if parsed would result
+    in the exact same AST except for nodes location.
+
+    Warning: It is guaranteed that this function will always produce stable results.
+    However, it's not guaranteed that it will stay the same between different
+    releases due to bugfixes or changes in the GraphQL specification.
+    """ '''
+
+    Query example::
+
+        query SomeQuery($foo: String!, $bar: String) {
+          someField(foo: $foo, bar: $bar) {
+            a
+            b {
+              c
+              d
+            }
+          }
+        }
+
+    Becomes::
+
+        query SomeQuery($foo:String!$bar:String){someField(foo:$foo bar:$bar){a b{c d}}}
+
+    SDL example::
+
+        """
+        Type description
+        """
+        type Foo {
+          """
+          Field description
+          """
+          bar: String
+        }
+
+    Becomes::
+
+        """Type description""" type Foo{"""Field description""" bar:String}
+    '''
+    source_obj = Source(source) if isinstance(source, str) else source
+    if not isinstance(source_obj, Source):
+        raise TypeError(
+            f"Must provide string or Source. Received: {inspect(source_obj)}."
+        )
+
+    body = source_obj.body
+    lexer = Lexer(source_obj)
+    stripped_body = ""
+    was_last_added_token_non_punctuator = False
+    while lexer.advance().kind != TokenKind.EOF:
+        current_token = lexer.token
+        token_kind = current_token.kind
+
+        # Every two non-punctuator tokens should have space between them.
+        # Also prevent case of non-punctuator token following by spread resulting
+        # in invalid token (e.g.`1...` is invalid Float token).
+        is_non_punctuator = not is_punctuator_token_kind(current_token.kind)
+        if was_last_added_token_non_punctuator:
+            if is_non_punctuator or current_token.kind == TokenKind.SPREAD:
+                stripped_body += " "
+
+        token_body = body[current_token.start : current_token.end]
+        if token_kind == TokenKind.BLOCK_STRING:
+            stripped_body += dedent_block_string(token_body)
+        else:
+            stripped_body += token_body
+
+        was_last_added_token_non_punctuator = is_non_punctuator
+
+    return stripped_body
+
+
+def dedent_block_string(block_str: str) -> str:
+    """Skip leading and trailing triple quotations"""
+    raw_str = block_str[3:-3]
+    body = dedent_block_string_value(raw_str)
+
+    lines = body.splitlines()
+    if get_block_string_indentation(lines) > 0:
+        body = "\n" + body
+
+    last_char = body[-1:]
+    has_trailing_quote = last_char == '"' and body[-4:] != '\\"""'
+    if has_trailing_quote or last_char == "\\":
+        body += "\n"
+
+    return '"""' + body + '"""'
diff --git a/src/graphql/utilities/type_comparators.py b/src/graphql/utilities/type_comparators.py
new file mode 100644
index 0000000..6288378
--- /dev/null
+++ b/src/graphql/utilities/type_comparators.py
@@ -0,0 +1,131 @@
+from typing import cast
+
+from ..type import (
+    GraphQLAbstractType,
+    GraphQLCompositeType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLType,
+    is_abstract_type,
+    is_interface_type,
+    is_list_type,
+    is_non_null_type,
+    is_object_type,
+)
+
+__all__ = ["is_equal_type", "is_type_sub_type_of", "do_types_overlap"]
+
+
+def is_equal_type(type_a: GraphQLType, type_b: GraphQLType) -> bool:
+    """Check whether two types are equal.
+
+    Provided two types, return true if the types are equal (invariant)."""
+    # Equivalent types are equal.
+    if type_a is type_b:
+        return True
+
+    # If either type is non-null, the other must also be non-null.
+    if is_non_null_type(type_a) and is_non_null_type(type_b):
+        # noinspection PyUnresolvedReferences
+        return is_equal_type(type_a.of_type, type_b.of_type)  # type:ignore
+
+    # If either type is a list, the other must also be a list.
+    if is_list_type(type_a) and is_list_type(type_b):
+        # noinspection PyUnresolvedReferences
+        return is_equal_type(type_a.of_type, type_b.of_type)  # type:ignore
+
+    # Otherwise the types are not equal.
+    return False
+
+
+def is_type_sub_type_of(
+    schema: GraphQLSchema, maybe_subtype: GraphQLType, super_type: GraphQLType
+) -> bool:
+    """Check whether a type is subtype of another type in a given schema.
+
+    Provided a type and a super type, return true if the first type is either equal or
+    a subset of the second super type (covariant).
+    """
+    # Equivalent type is a valid subtype
+    if maybe_subtype is super_type:
+        return True
+
+    # If super_type is non-null, maybe_subtype must also be non-null.
+    if is_non_null_type(super_type):
+        if is_non_null_type(maybe_subtype):
+            return is_type_sub_type_of(
+                schema,
+                cast(GraphQLNonNull, maybe_subtype).of_type,
+                cast(GraphQLNonNull, super_type).of_type,
+            )
+        return False
+    elif is_non_null_type(maybe_subtype):
+        # If super_type is nullable, maybe_subtype may be non-null or nullable.
+        return is_type_sub_type_of(
+            schema, cast(GraphQLNonNull, maybe_subtype).of_type, super_type
+        )
+
+    # If super_type type is a list, maybeSubType type must also be a list.
+    if is_list_type(super_type):
+        if is_list_type(maybe_subtype):
+            return is_type_sub_type_of(
+                schema,
+                cast(GraphQLList, maybe_subtype).of_type,
+                cast(GraphQLList, super_type).of_type,
+            )
+        return False
+    elif is_list_type(maybe_subtype):
+        # If super_type is not a list, maybe_subtype must also be not a list.
+        return False
+
+    # If super_type type is abstract, check if it is super type of maybe_subtype.
+    # Otherwise, the child type is not a valid subtype of the parent type.
+    return (
+        is_abstract_type(super_type)
+        and (is_interface_type(maybe_subtype) or is_object_type(maybe_subtype))
+        and schema.is_sub_type(
+            cast(GraphQLAbstractType, super_type),
+            cast(GraphQLObjectType, maybe_subtype),
+        )
+    )
+
+
+def do_types_overlap(
+    schema: GraphQLSchema, type_a: GraphQLCompositeType, type_b: GraphQLCompositeType
+) -> bool:
+    """Check whether two types overlap in a given schema.
+
+    Provided two composite types, determine if they "overlap". Two composite types
+    overlap when the Sets of possible concrete types for each intersect.
+
+    This is often used to determine if a fragment of a given type could possibly be
+    visited in a context of another type.
+
+    This function is commutative.
+    """
+    # Equivalent types overlap
+    if type_a is type_b:
+        return True
+
+    if is_abstract_type(type_a):
+        type_a = cast(GraphQLAbstractType, type_a)
+        if is_abstract_type(type_b):
+            # If both types are abstract, then determine if there is any intersection
+            # between possible concrete types of each.
+            type_b = cast(GraphQLAbstractType, type_b)
+            return any(
+                schema.is_sub_type(type_b, type_)
+                for type_ in schema.get_possible_types(type_a)
+            )
+        # Determine if latter type is a possible concrete type of the former.
+        return schema.is_sub_type(type_a, type_b)
+
+    if is_abstract_type(type_b):
+        # Determine if former type is a possible concrete type of the latter.
+        type_b = cast(GraphQLAbstractType, type_b)
+        return schema.is_sub_type(type_b, type_a)
+
+    # Otherwise the types do not overlap.
+    return False
diff --git a/src/graphql/utilities/type_from_ast.py b/src/graphql/utilities/type_from_ast.py
new file mode 100644
index 0000000..a837a85
--- /dev/null
+++ b/src/graphql/utilities/type_from_ast.py
@@ -0,0 +1,64 @@
+from typing import cast, overload, Optional
+
+from ..language import ListTypeNode, NamedTypeNode, NonNullTypeNode, TypeNode
+from ..pyutils import inspect
+from ..type import (
+    GraphQLSchema,
+    GraphQLNamedType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLNullableType,
+    GraphQLType,
+)
+
+__all__ = ["type_from_ast"]
+
+
+@overload
+def type_from_ast(
+    schema: GraphQLSchema, type_node: NamedTypeNode
+) -> Optional[GraphQLNamedType]:
+    ...
+
+
+@overload
+def type_from_ast(
+    schema: GraphQLSchema, type_node: ListTypeNode
+) -> Optional[GraphQLList]:
+    ...
+
+
+@overload
+def type_from_ast(
+    schema: GraphQLSchema, type_node: NonNullTypeNode
+) -> Optional[GraphQLNonNull]:
+    ...
+
+
+@overload
+def type_from_ast(schema: GraphQLSchema, type_node: TypeNode) -> Optional[GraphQLType]:
+    ...
+
+
+def type_from_ast(schema: GraphQLSchema, type_node: TypeNode,) -> Optional[GraphQLType]:
+    """Get the GraphQL type definition from an AST node.
+
+    Given a Schema and an AST node describing a type, return a GraphQLType definition
+    which applies to that type. For example, if provided the parsed AST node for
+    ``[User]``, a GraphQLList instance will be returned, containing the type called
+    "User" found in the schema. If a type called "User" is not found in the schema,
+    then None will be returned.
+    """
+    inner_type: Optional[GraphQLType]
+    if isinstance(type_node, ListTypeNode):
+        inner_type = type_from_ast(schema, type_node.type)
+        return GraphQLList(inner_type) if inner_type else None
+    if isinstance(type_node, NonNullTypeNode):
+        inner_type = type_from_ast(schema, type_node.type)
+        inner_type = cast(GraphQLNullableType, inner_type)
+        return GraphQLNonNull(inner_type) if inner_type else None
+    if isinstance(type_node, NamedTypeNode):
+        return schema.get_type(type_node.name.value)
+
+    # Not reachable. All possible type nodes have been considered.
+    raise TypeError(f"Unexpected type node: {inspect(type_node)}.")
diff --git a/src/graphql/utilities/type_info.py b/src/graphql/utilities/type_info.py
new file mode 100644
index 0000000..847e573
--- /dev/null
+++ b/src/graphql/utilities/type_info.py
@@ -0,0 +1,322 @@
+from typing import Any, Callable, List, Optional, Union, cast
+
+from ..language import (
+    ArgumentNode,
+    DirectiveNode,
+    EnumValueNode,
+    FieldNode,
+    InlineFragmentNode,
+    ListValueNode,
+    Node,
+    ObjectFieldNode,
+    OperationDefinitionNode,
+    SelectionSetNode,
+    VariableDefinitionNode,
+    Visitor,
+)
+from ..pyutils import Undefined
+from ..type import (
+    GraphQLArgument,
+    GraphQLCompositeType,
+    GraphQLDirective,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputObjectType,
+    GraphQLInputType,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLObjectType,
+    GraphQLOutputType,
+    GraphQLSchema,
+    GraphQLType,
+    is_composite_type,
+    is_input_type,
+    is_output_type,
+    get_named_type,
+    SchemaMetaFieldDef,
+    TypeMetaFieldDef,
+    TypeNameMetaFieldDef,
+    is_object_type,
+    is_interface_type,
+    get_nullable_type,
+    is_list_type,
+    is_input_object_type,
+    is_enum_type,
+)
+from .type_from_ast import type_from_ast
+
+__all__ = ["TypeInfo", "TypeInfoVisitor"]
+
+
+GetFieldDefType = Callable[
+    [GraphQLSchema, GraphQLType, FieldNode], Optional[GraphQLField]
+]
+
+
+class TypeInfo:
+    """Utility class for keeping track of type definitions.
+
+    TypeInfo is a utility class which, given a GraphQL schema, can keep track of the
+    current field and type definitions at any point in a GraphQL document AST during
+    a recursive descent by calling :meth:`enter(node) <.TypeInfo.enter>` and
+    :meth:`leave(node) <.TypeInfo.leave>`.
+    """
+
+    def __init__(
+        self,
+        schema: GraphQLSchema,
+        get_field_def_fn: Optional[GetFieldDefType] = None,
+        initial_type: Optional[GraphQLType] = None,
+    ) -> None:
+        """Initialize the TypeInfo for the given GraphQL schema.
+
+        The experimental optional second parameter is only needed in order to support
+        non-spec-compliant code bases. You should never need to use it. It may disappear
+        in the future.
+
+        Initial type may be provided in rare cases to facilitate traversals beginning
+        somewhere other than documents.
+        """
+        self._schema = schema
+        self._type_stack: List[Optional[GraphQLOutputType]] = []
+        self._parent_type_stack: List[Optional[GraphQLCompositeType]] = []
+        self._input_type_stack: List[Optional[GraphQLInputType]] = []
+        self._field_def_stack: List[Optional[GraphQLField]] = []
+        self._default_value_stack: List[Any] = []
+        self._directive: Optional[GraphQLDirective] = None
+        self._argument: Optional[GraphQLArgument] = None
+        self._enum_value: Optional[GraphQLEnumValue] = None
+        self._get_field_def = get_field_def_fn or get_field_def
+        if initial_type:
+            if is_input_type(initial_type):
+                self._input_type_stack.append(cast(GraphQLInputType, initial_type))
+            if is_composite_type(initial_type):
+                self._parent_type_stack.append(cast(GraphQLCompositeType, initial_type))
+            if is_output_type(initial_type):
+                self._type_stack.append(cast(GraphQLOutputType, initial_type))
+
+    def get_type(self) -> Optional[GraphQLOutputType]:
+        if self._type_stack:
+            return self._type_stack[-1]
+        return None
+
+    def get_parent_type(self) -> Optional[GraphQLCompositeType]:
+        if self._parent_type_stack:
+            return self._parent_type_stack[-1]
+        return None
+
+    def get_input_type(self) -> Optional[GraphQLInputType]:
+        if self._input_type_stack:
+            return self._input_type_stack[-1]
+        return None
+
+    def get_parent_input_type(self) -> Optional[GraphQLInputType]:
+        if len(self._input_type_stack) > 1:
+            return self._input_type_stack[-2]
+        return None
+
+    def get_field_def(self) -> Optional[GraphQLField]:
+        if self._field_def_stack:
+            return self._field_def_stack[-1]
+        return None
+
+    def get_default_value(self) -> Any:
+        if self._default_value_stack:
+            return self._default_value_stack[-1]
+        return None
+
+    def get_directive(self) -> Optional[GraphQLDirective]:
+        return self._directive
+
+    def get_argument(self) -> Optional[GraphQLArgument]:
+        return self._argument
+
+    def get_enum_value(self) -> Optional[GraphQLEnumValue]:
+        return self._enum_value
+
+    def enter(self, node: Node) -> None:
+        method = getattr(self, "enter_" + node.kind, None)
+        if method:
+            method(node)
+
+    def leave(self, node: Node) -> None:
+        method = getattr(self, "leave_" + node.kind, None)
+        if method:
+            method()
+
+    # noinspection PyUnusedLocal
+    def enter_selection_set(self, node: SelectionSetNode) -> None:
+        named_type = get_named_type(self.get_type())
+        self._parent_type_stack.append(
+            cast(GraphQLCompositeType, named_type)
+            if is_composite_type(named_type)
+            else None
+        )
+
+    def enter_field(self, node: FieldNode) -> None:
+        parent_type = self.get_parent_type()
+        if parent_type:
+            field_def = self._get_field_def(self._schema, parent_type, node)
+            field_type = field_def.type if field_def else None
+        else:
+            field_def = field_type = None
+        self._field_def_stack.append(field_def)
+        self._type_stack.append(field_type if is_output_type(field_type) else None)
+
+    def enter_directive(self, node: DirectiveNode) -> None:
+        self._directive = self._schema.get_directive(node.name.value)
+
+    def enter_operation_definition(self, node: OperationDefinitionNode) -> None:
+        type_ = getattr(self._schema, f"{node.operation.value}_type")
+        self._type_stack.append(type_ if is_object_type(type_) else None)
+
+    def enter_inline_fragment(self, node: InlineFragmentNode) -> None:
+        type_condition_ast = node.type_condition
+        output_type = (
+            type_from_ast(self._schema, type_condition_ast)
+            if type_condition_ast
+            else get_named_type(self.get_type())
+        )
+        self._type_stack.append(
+            cast(GraphQLOutputType, output_type)
+            if is_output_type(output_type)
+            else None
+        )
+
+    enter_fragment_definition = enter_inline_fragment
+
+    def enter_variable_definition(self, node: VariableDefinitionNode) -> None:
+        input_type = type_from_ast(self._schema, node.type)
+        self._input_type_stack.append(
+            cast(GraphQLInputType, input_type) if is_input_type(input_type) else None
+        )
+
+    def enter_argument(self, node: ArgumentNode) -> None:
+        field_or_directive = self.get_directive() or self.get_field_def()
+        if field_or_directive:
+            arg_def = field_or_directive.args.get(node.name.value)
+            arg_type = arg_def.type if arg_def else None
+        else:
+            arg_def = arg_type = None
+        self._argument = arg_def
+        self._default_value_stack.append(
+            arg_def.default_value if arg_def else Undefined
+        )
+        self._input_type_stack.append(arg_type if is_input_type(arg_type) else None)
+
+    # noinspection PyUnusedLocal
+    def enter_list_value(self, node: ListValueNode) -> None:
+        list_type = get_nullable_type(self.get_input_type())  # type: ignore
+        item_type = (
+            cast(GraphQLList, list_type).of_type
+            if is_list_type(list_type)
+            else list_type
+        )
+        # List positions never have a default value.
+        self._default_value_stack.append(Undefined)
+        self._input_type_stack.append(item_type if is_input_type(item_type) else None)
+
+    def enter_object_field(self, node: ObjectFieldNode) -> None:
+        object_type = get_named_type(self.get_input_type())
+        if is_input_object_type(object_type):
+            input_field = cast(GraphQLInputObjectType, object_type).fields.get(
+                node.name.value
+            )
+            input_field_type = input_field.type if input_field else None
+        else:
+            input_field = input_field_type = None
+        self._default_value_stack.append(
+            input_field.default_value if input_field else Undefined
+        )
+        self._input_type_stack.append(
+            input_field_type if is_input_type(input_field_type) else None
+        )
+
+    def enter_enum_value(self, node: EnumValueNode) -> None:
+        enum_type = get_named_type(self.get_input_type())
+        if is_enum_type(enum_type):
+            enum_value = cast(GraphQLEnumType, enum_type).values.get(node.value)
+        else:
+            enum_value = None
+        self._enum_value = enum_value
+
+    def leave_selection_set(self) -> None:
+        del self._parent_type_stack[-1:]
+
+    def leave_field(self) -> None:
+        del self._field_def_stack[-1:]
+        del self._type_stack[-1:]
+
+    def leave_directive(self) -> None:
+        self._directive = None
+
+    def leave_operation_definition(self) -> None:
+        del self._type_stack[-1:]
+
+    leave_inline_fragment = leave_operation_definition
+    leave_fragment_definition = leave_operation_definition
+
+    def leave_variable_definition(self) -> None:
+        del self._input_type_stack[-1:]
+
+    def leave_argument(self) -> None:
+        self._argument = None
+        del self._default_value_stack[-1:]
+        del self._input_type_stack[-1:]
+
+    def leave_list_value(self) -> None:
+        del self._default_value_stack[-1:]
+        del self._input_type_stack[-1:]
+
+    leave_object_field = leave_list_value
+
+    def leave_enum_value(self) -> None:
+        self._enum_value = None
+
+
+def get_field_def(
+    schema: GraphQLSchema, parent_type: GraphQLType, field_node: FieldNode
+) -> Optional[GraphQLField]:
+    """Get field definition.
+
+    Not exactly the same as the executor's definition of
+    :func:`graphql.execution.get_field_def`, in this statically evaluated environment
+    we do not always have an Object type, and need to handle Interface and Union types.
+    """
+    name = field_node.name.value
+    if name == "__schema" and schema.query_type is parent_type:
+        return SchemaMetaFieldDef
+    if name == "__type" and schema.query_type is parent_type:
+        return TypeMetaFieldDef
+    if name == "__typename" and is_composite_type(parent_type):
+        return TypeNameMetaFieldDef
+    if is_object_type(parent_type) or is_interface_type(parent_type):
+        parent_type = cast(Union[GraphQLObjectType, GraphQLInterfaceType], parent_type)
+        return parent_type.fields.get(name)
+    return None
+
+
+class TypeInfoVisitor(Visitor):
+    """A visitor which maintains a provided TypeInfo."""
+
+    def __init__(self, type_info: "TypeInfo", visitor: Visitor):
+        self.type_info = type_info
+        self.visitor = visitor
+
+    def enter(self, node: Node, *args: Any) -> Any:
+        self.type_info.enter(node)
+        fn = self.visitor.get_visit_fn(node.kind)
+        if fn:
+            result = fn(node, *args)
+            if result is not None:
+                self.type_info.leave(node)
+                if isinstance(result, Node):
+                    self.type_info.enter(result)
+            return result
+
+    def leave(self, node: Node, *args: Any) -> Any:
+        fn = self.visitor.get_visit_fn(node.kind, is_leaving=True)
+        result = fn(node, *args) if fn else None
+        self.type_info.leave(node)
+        return result
diff --git a/src/graphql/utilities/value_from_ast.py b/src/graphql/utilities/value_from_ast.py
new file mode 100644
index 0000000..62a506c
--- /dev/null
+++ b/src/graphql/utilities/value_from_ast.py
@@ -0,0 +1,149 @@
+from typing import Any, Dict, List, Optional, cast
+
+from ..language import (
+    ListValueNode,
+    NullValueNode,
+    ObjectValueNode,
+    ValueNode,
+    VariableNode,
+)
+from ..pyutils import inspect, Undefined
+from ..type import (
+    GraphQLInputObjectType,
+    GraphQLInputType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLScalarType,
+    is_input_object_type,
+    is_leaf_type,
+    is_list_type,
+    is_non_null_type,
+)
+
+__all__ = ["value_from_ast"]
+
+
+def value_from_ast(
+    value_node: Optional[ValueNode],
+    type_: GraphQLInputType,
+    variables: Optional[Dict[str, Any]] = None,
+) -> Any:
+    """Produce a Python value given a GraphQL Value AST.
+
+    A GraphQL type must be provided, which will be used to interpret different GraphQL
+    Value literals.
+
+    Returns ``Undefined`` when the value could not be validly coerced according
+    to the provided type.
+
+    =================== ============== ================
+       GraphQL Value      JSON Value     Python Value
+    =================== ============== ================
+       Input Object       Object         dict
+       List               Array          list
+       Boolean            Boolean        bool
+       String             String         str
+       Int / Float        Number         int / float
+       Enum Value         Mixed          Any
+       NullValue          null           None
+    =================== ============== ================
+
+    """
+    if not value_node:
+        # When there is no node, then there is also no value.
+        # Importantly, this is different from returning the value null.
+        return Undefined
+
+    if isinstance(value_node, VariableNode):
+        variable_name = value_node.name.value
+        if not variables:
+            return Undefined
+        variable_value = variables.get(variable_name, Undefined)
+        if variable_value is None and is_non_null_type(type_):
+            return Undefined
+        # Note: This does no further checking that this variable is correct.
+        # This assumes that this query has been validated and the variable usage here
+        # is of the correct type.
+        return variable_value
+
+    if is_non_null_type(type_):
+        if isinstance(value_node, NullValueNode):
+            return Undefined
+        type_ = cast(GraphQLNonNull, type_)
+        return value_from_ast(value_node, type_.of_type, variables)
+
+    if isinstance(value_node, NullValueNode):
+        return None  # This is explicitly returning the value None.
+
+    if is_list_type(type_):
+        type_ = cast(GraphQLList, type_)
+        item_type = type_.of_type
+        if isinstance(value_node, ListValueNode):
+            coerced_values: List[Any] = []
+            append_value = coerced_values.append
+            for item_node in value_node.values:
+                if is_missing_variable(item_node, variables):
+                    # If an array contains a missing variable, it is either coerced to
+                    # None or if the item type is non-null, it is considered invalid.
+                    if is_non_null_type(item_type):
+                        return Undefined
+                    append_value(None)
+                else:
+                    item_value = value_from_ast(item_node, item_type, variables)
+                    if item_value is Undefined:
+                        return Undefined
+                    append_value(item_value)
+            return coerced_values
+        coerced_value = value_from_ast(value_node, item_type, variables)
+        if coerced_value is Undefined:
+            return Undefined
+        return [coerced_value]
+
+    if is_input_object_type(type_):
+        if not isinstance(value_node, ObjectValueNode):
+            return Undefined
+        type_ = cast(GraphQLInputObjectType, type_)
+        coerced_obj: Dict[str, Any] = {}
+        fields = type_.fields
+        field_nodes = {field.name.value: field for field in value_node.fields}
+        for field_name, field in fields.items():
+            field_node = field_nodes.get(field_name)
+            if not field_node or is_missing_variable(field_node.value, variables):
+                if field.default_value is not Undefined:
+                    # Use out name as name if it exists (extension of GraphQL.js).
+                    coerced_obj[field.out_name or field_name] = field.default_value
+                elif is_non_null_type(field.type):  # pragma: no cover else
+                    return Undefined
+                continue
+            field_value = value_from_ast(field_node.value, field.type, variables)
+            if field_value is Undefined:
+                return Undefined
+            coerced_obj[field.out_name or field_name] = field_value
+
+        return type_.out_type(coerced_obj)
+
+    if is_leaf_type(type_):
+        # Scalars fulfill parsing a literal value via `parse_literal()`. Invalid values
+        # represent a failure to parse correctly, in which case Undefined is returned.
+        type_ = cast(GraphQLScalarType, type_)
+        # noinspection PyBroadException
+        try:
+            if variables:
+                result = type_.parse_literal(value_node, variables)
+            else:
+                result = type_.parse_literal(value_node)
+        except Exception:
+            return Undefined
+        return result
+
+    # Not reachable. All possible input types have been considered.
+    raise TypeError(f"Unexpected input type: {inspect(type_)}.")
+
+
+def is_missing_variable(
+    value_node: ValueNode, variables: Optional[Dict[str, Any]] = None
+) -> bool:
+    """Check if ``value_node`` is a variable not defined in the ``variables`` dict."""
+    return isinstance(value_node, VariableNode) and (
+        not variables or variables.get(value_node.name.value, Undefined) is Undefined
+    )
diff --git a/src/graphql/utilities/value_from_ast_untyped.py b/src/graphql/utilities/value_from_ast_untyped.py
new file mode 100644
index 0000000..7ac40c9
--- /dev/null
+++ b/src/graphql/utilities/value_from_ast_untyped.py
@@ -0,0 +1,110 @@
+from math import nan
+from typing import Any, Callable, Dict, Optional, Union
+
+from ..language import (
+    ValueNode,
+    BooleanValueNode,
+    EnumValueNode,
+    FloatValueNode,
+    IntValueNode,
+    ListValueNode,
+    NullValueNode,
+    ObjectValueNode,
+    StringValueNode,
+    VariableNode,
+)
+
+from ..pyutils import inspect, Undefined
+
+__all__ = ["value_from_ast_untyped"]
+
+
+def value_from_ast_untyped(
+    value_node: ValueNode, variables: Optional[Dict[str, Any]] = None
+) -> Any:
+    """Produce a Python value given a GraphQL Value AST.
+
+    Unlike :func:`~graphql.value_from_ast`, no type is provided.
+    The resulting Python value will reflect the provided GraphQL value AST.
+
+    =================== ============== ================
+       GraphQL Value      JSON Value     Python Value
+    =================== ============== ================
+       Input Object       Object         dict
+       List               Array          list
+       Boolean            Boolean        bool
+       String / Enum      String         str
+       Int / Float        Number         int / float
+       Null               null           None
+    =================== ============== ================
+
+    """
+    func = _value_from_kind_functions.get(value_node.kind)
+    if func:
+        return func(value_node, variables)
+
+    # Not reachable. All possible value nodes have been considered.
+    raise TypeError(  # pragma: no cover
+        f"Unexpected value node: {inspect(value_node)}."
+    )
+
+
+def value_from_null(_value_node: NullValueNode, _variables: Any) -> Any:
+    return None
+
+
+def value_from_int(value_node: IntValueNode, _variables: Any) -> Any:
+    try:
+        return int(value_node.value)
+    except ValueError:
+        return nan
+
+
+def value_from_float(value_node: FloatValueNode, _variables: Any) -> Any:
+    try:
+        return float(value_node.value)
+    except ValueError:
+        return nan
+
+
+def value_from_string(
+    value_node: Union[BooleanValueNode, EnumValueNode, StringValueNode], _variables: Any
+) -> Any:
+    return value_node.value
+
+
+def value_from_list(
+    value_node: ListValueNode, variables: Optional[Dict[str, Any]]
+) -> Any:
+    return [value_from_ast_untyped(node, variables) for node in value_node.values]
+
+
+def value_from_object(
+    value_node: ObjectValueNode, variables: Optional[Dict[str, Any]]
+) -> Any:
+    return {
+        field.name.value: value_from_ast_untyped(field.value, variables)
+        for field in value_node.fields
+    }
+
+
+def value_from_variable(
+    value_node: VariableNode, variables: Optional[Dict[str, Any]]
+) -> Any:
+    variable_name = value_node.name.value
+    if not variables:
+        return Undefined
+    return variables.get(variable_name, Undefined)
+
+
+_value_from_kind_functions: Dict[str, Callable] = {
+    "null_value": value_from_null,
+    "int_value": value_from_int,
+    "float_value": value_from_float,
+    "string_value": value_from_string,
+    "enum_value": value_from_string,
+    "boolean_value": value_from_string,
+    "list_value": value_from_list,
+    "object_value": value_from_object,
+    "variable": value_from_variable,
+}
diff --git a/src/graphql/validation/__init__.py b/src/graphql/validation/__init__.py
new file mode 100644
index 0000000..048dc16
--- /dev/null
+++ b/src/graphql/validation/__init__.py
@@ -0,0 +1,149 @@
+"""GraphQL Validation
+
+The :mod:`graphql.validation` package fulfills the Validation phase of fulfilling a
+GraphQL result.
+"""
+
+from .validate import validate
+
+from .validation_context import (
+    ASTValidationContext,
+    SDLValidationContext,
+    ValidationContext,
+)
+
+from .rules import ValidationRule, ASTValidationRule, SDLValidationRule
+
+# All validation rules in the GraphQL Specification.
+from .specified_rules import specified_rules
+
+# Spec Section: "Executable Definitions"
+from .rules.executable_definitions import ExecutableDefinitionsRule
+
+# Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
+from .rules.fields_on_correct_type import FieldsOnCorrectTypeRule
+
+# Spec Section: "Fragments on Composite Types"
+from .rules.fragments_on_composite_types import FragmentsOnCompositeTypesRule
+
+# Spec Section: "Argument Names"
+from .rules.known_argument_names import KnownArgumentNamesRule
+
+# Spec Section: "Directives Are Defined"
+from .rules.known_directives import KnownDirectivesRule
+
+# Spec Section: "Fragment spread target defined"
+from .rules.known_fragment_names import KnownFragmentNamesRule
+
+# Spec Section: "Fragment Spread Type Existence"
+from .rules.known_type_names import KnownTypeNamesRule
+
+# Spec Section: "Lone Anonymous Operation"
+from .rules.lone_anonymous_operation import LoneAnonymousOperationRule
+
+# Spec Section: "Fragments must not form cycles"
+from .rules.no_fragment_cycles import NoFragmentCyclesRule
+
+# Spec Section: "All Variable Used Defined"
+from .rules.no_undefined_variables import NoUndefinedVariablesRule
+
+# Spec Section: "Fragments must be used"
+from .rules.no_unused_fragments import NoUnusedFragmentsRule
+
+# Spec Section: "All Variables Used"
+from .rules.no_unused_variables import NoUnusedVariablesRule
+
+# Spec Section: "Field Selection Merging"
+from .rules.overlapping_fields_can_be_merged import OverlappingFieldsCanBeMergedRule
+
+# Spec Section: "Fragment spread is possible"
+from .rules.possible_fragment_spreads import PossibleFragmentSpreadsRule
+
+# Spec Section: "Argument Optionality"
+from .rules.provided_required_arguments import ProvidedRequiredArgumentsRule
+
+# Spec Section: "Leaf Field Selections"
+from .rules.scalar_leafs import ScalarLeafsRule
+
+# Spec Section: "Subscriptions with Single Root Field"
+from .rules.single_field_subscriptions import SingleFieldSubscriptionsRule
+
+# Spec Section: "Argument Uniqueness"
+from .rules.unique_argument_names import UniqueArgumentNamesRule
+
+# Spec Section: "Directives Are Unique Per Location"
+from .rules.unique_directives_per_location import UniqueDirectivesPerLocationRule
+
+# Spec Section: "Fragment Name Uniqueness"
+from .rules.unique_fragment_names import UniqueFragmentNamesRule
+
+# Spec Section: "Input Object Field Uniqueness"
+from .rules.unique_input_field_names import UniqueInputFieldNamesRule
+
+# Spec Section: "Operation Name Uniqueness"
+from .rules.unique_operation_names import UniqueOperationNamesRule
+
+# Spec Section: "Variable Uniqueness"
+from .rules.unique_variable_names import UniqueVariableNamesRule
+
+# Spec Section: "Value Type Correctness"
+from .rules.values_of_correct_type import ValuesOfCorrectTypeRule
+
+# Spec Section: "Variables are Input Types"
+from .rules.variables_are_input_types import VariablesAreInputTypesRule
+
+# Spec Section: "All Variable Usages Are Allowed"
+from .rules.variables_in_allowed_position import VariablesInAllowedPositionRule
+
+# SDL-specific validation rules
+from .rules.lone_schema_definition import LoneSchemaDefinitionRule
+from .rules.unique_operation_types import UniqueOperationTypesRule
+from .rules.unique_type_names import UniqueTypeNamesRule
+from .rules.unique_enum_value_names import UniqueEnumValueNamesRule
+from .rules.unique_field_definition_names import UniqueFieldDefinitionNamesRule
+from .rules.unique_directive_names import UniqueDirectiveNamesRule
+from .rules.possible_type_extensions import PossibleTypeExtensionsRule
+
+__all__ = [
+    "validate",
+    "ASTValidationContext",
+    "ASTValidationRule",
+    "SDLValidationContext",
+    "SDLValidationRule",
+    "ValidationContext",
+    "ValidationRule",
+    "specified_rules",
+    "ExecutableDefinitionsRule",
+    "FieldsOnCorrectTypeRule",
+    "FragmentsOnCompositeTypesRule",
+    "KnownArgumentNamesRule",
+    "KnownDirectivesRule",
+    "KnownFragmentNamesRule",
+    "KnownTypeNamesRule",
+    "LoneAnonymousOperationRule",
+    "NoFragmentCyclesRule",
+    "NoUndefinedVariablesRule",
+    "NoUnusedFragmentsRule",
+    "NoUnusedVariablesRule",
+    "OverlappingFieldsCanBeMergedRule",
+    "PossibleFragmentSpreadsRule",
+    "ProvidedRequiredArgumentsRule",
+    "ScalarLeafsRule",
+    "SingleFieldSubscriptionsRule",
+    "UniqueArgumentNamesRule",
+    "UniqueDirectivesPerLocationRule",
+    "UniqueFragmentNamesRule",
+    "UniqueInputFieldNamesRule",
+    "UniqueOperationNamesRule",
+    "UniqueVariableNamesRule",
+    "ValuesOfCorrectTypeRule",
+    "VariablesAreInputTypesRule",
+    "VariablesInAllowedPositionRule",
+    "LoneSchemaDefinitionRule",
+    "UniqueOperationTypesRule",
+    "UniqueTypeNamesRule",
+    "UniqueEnumValueNamesRule",
+    "UniqueFieldDefinitionNamesRule",
+    "UniqueDirectiveNamesRule",
+    "PossibleTypeExtensionsRule",
+]
diff --git a/src/graphql/validation/rules/__init__.py b/src/graphql/validation/rules/__init__.py
new file mode 100644
index 0000000..5415874
--- /dev/null
+++ b/src/graphql/validation/rules/__init__.py
@@ -0,0 +1,41 @@
+"""graphql.validation.rules package"""
+
+from ...error import GraphQLError
+from ...language.visitor import Visitor
+from ..validation_context import (
+    ASTValidationContext,
+    SDLValidationContext,
+    ValidationContext,
+)
+
+__all__ = ["ASTValidationRule", "SDLValidationRule", "ValidationRule"]
+
+
+class ASTValidationRule(Visitor):
+    """Visitor for validation of an AST."""
+
+    context: ASTValidationContext
+
+    def __init__(self, context: ASTValidationContext):
+        self.context = context
+
+    def report_error(self, error: GraphQLError) -> None:
+        self.context.report_error(error)
+
+
+class SDLValidationRule(ASTValidationRule):
+    """Visitor for validation of an SDL AST."""
+
+    context: SDLValidationContext
+
+    def __init__(self, context: SDLValidationContext) -> None:
+        super().__init__(context)
+
+
+class ValidationRule(ASTValidationRule):
+    """Visitor for validation using a GraphQL schema."""
+
+    context: ValidationContext
+
+    def __init__(self, context: ValidationContext) -> None:
+        super().__init__(context)
diff --git a/src/graphql/validation/rules/executable_definitions.py b/src/graphql/validation/rules/executable_definitions.py
new file mode 100644
index 0000000..6946988
--- /dev/null
+++ b/src/graphql/validation/rules/executable_definitions.py
@@ -0,0 +1,46 @@
+from typing import Any, Union, cast
+
+from ...error import GraphQLError
+from ...language import (
+    DirectiveDefinitionNode,
+    DocumentNode,
+    ExecutableDefinitionNode,
+    SchemaDefinitionNode,
+    SchemaExtensionNode,
+    TypeDefinitionNode,
+    VisitorAction,
+    SKIP,
+)
+from . import ASTValidationRule
+
+__all__ = ["ExecutableDefinitionsRule"]
+
+
+class ExecutableDefinitionsRule(ASTValidationRule):
+    """Executable definitions
+
+    A GraphQL document is only valid for execution if all definitions are either
+    operation or fragment definitions.
+    """
+
+    def enter_document(self, node: DocumentNode, *_args: Any) -> VisitorAction:
+        for definition in node.definitions:
+            if not isinstance(definition, ExecutableDefinitionNode):
+                def_name = (
+                    "schema"
+                    if isinstance(
+                        definition, (SchemaDefinitionNode, SchemaExtensionNode)
+                    )
+                    else "'{}'".format(
+                        cast(
+                            Union[DirectiveDefinitionNode, TypeDefinitionNode],
+                            definition,
+                        ).name.value
+                    )
+                )
+                self.report_error(
+                    GraphQLError(
+                        f"The {def_name} definition is not executable.", definition,
+                    )
+                )
+        return SKIP
diff --git a/src/graphql/validation/rules/fields_on_correct_type.py b/src/graphql/validation/rules/fields_on_correct_type.py
new file mode 100644
index 0000000..7c1c1aa
--- /dev/null
+++ b/src/graphql/validation/rules/fields_on_correct_type.py
@@ -0,0 +1,131 @@
+from collections import defaultdict
+from functools import cmp_to_key
+from typing import Any, Dict, List, Set, Union, cast
+
+from ...type import (
+    GraphQLAbstractType,
+    GraphQLInterfaceType,
+    GraphQLObjectType,
+    GraphQLOutputType,
+    GraphQLSchema,
+    is_abstract_type,
+    is_interface_type,
+    is_object_type,
+)
+from ...error import GraphQLError
+from ...language import FieldNode
+from ...pyutils import did_you_mean, suggestion_list
+from . import ValidationRule
+
+__all__ = ["FieldsOnCorrectTypeRule"]
+
+
+class FieldsOnCorrectTypeRule(ValidationRule):
+    """Fields on correct type
+
+    A GraphQL document is only valid if all fields selected are defined by the parent
+    type, or are an allowed meta field such as ``__typename``.
+    """
+
+    def enter_field(self, node: FieldNode, *_args: Any) -> None:
+        type_ = self.context.get_parent_type()
+        if not type_:
+            return
+        field_def = self.context.get_field_def()
+        if field_def:
+            return
+        # This field doesn't exist, lets look for suggestions.
+        schema = self.context.schema
+        field_name = node.name.value
+
+        # First determine if there are any suggested types to condition on.
+        suggestion = did_you_mean(
+            get_suggested_type_names(schema, type_, field_name),
+            "to use an inline fragment on",
+        )
+
+        # If there are no suggested types, then perhaps this was a typo?
+        if not suggestion:
+            suggestion = did_you_mean(get_suggested_field_names(type_, field_name))
+
+        # Report an error, including helpful suggestions.
+        self.report_error(
+            GraphQLError(
+                f"Cannot query field '{field_name}' on type '{type_}'." + suggestion,
+                node,
+            )
+        )
+
+
+def get_suggested_type_names(
+    schema: GraphQLSchema, type_: GraphQLOutputType, field_name: str
+) -> List[str]:
+    """
+    Get a list of suggested type names.
+
+    Go through all of the implementations of type, as well as the interfaces
+    that they implement. If any of those types include the provided field,
+    suggest them, sorted by how often the type is referenced.
+    """
+    if not is_abstract_type(type_):
+        # Must be an Object type, which does not have possible fields.
+        return []
+
+    type_ = cast(GraphQLAbstractType, type_)
+    suggested_types: Set[Union[GraphQLObjectType, GraphQLInterfaceType]] = set()
+    usage_count: Dict[str, int] = defaultdict(int)
+    for possible_type in schema.get_possible_types(type_):
+        if field_name not in possible_type.fields:
+            continue
+
+        # This object type defines this field.
+        suggested_types.add(possible_type)
+        usage_count[possible_type.name] = 1
+
+        for possible_interface in possible_type.interfaces:
+            if field_name not in possible_interface.fields:
+                continue
+
+            # This interface type defines this field.
+            suggested_types.add(possible_interface)
+            usage_count[possible_interface.name] += 1
+
+    def cmp(
+        type_a: Union[GraphQLObjectType, GraphQLInterfaceType],
+        type_b: Union[GraphQLObjectType, GraphQLInterfaceType],
+    ) -> int:  # pragma: no cover
+        # Suggest both interface and object types based on how common they are.
+        usage_count_diff = usage_count[type_b.name] - usage_count[type_a.name]
+        if usage_count_diff:
+            return usage_count_diff
+
+        # Suggest super types first followed by subtypes
+        if is_interface_type(type_a) and schema.is_sub_type(
+            cast(GraphQLInterfaceType, type_a), type_b
+        ):
+            return -1
+        if is_interface_type(type_b) and schema.is_sub_type(
+            cast(GraphQLInterfaceType, type_b), type_a
+        ):
+            return 1
+
+        if type_a.name > type_b.name:
+            return 1
+        if type_a.name < type_b.name:
+            return -1
+        return 0
+
+    return [type_.name for type_ in sorted(suggested_types, key=cmp_to_key(cmp))]
+
+
+def get_suggested_field_names(type_: GraphQLOutputType, field_name: str) -> List[str]:
+    """Get a list of suggested field names.
+
+    For the field name provided, determine if there are any similar field names that may
+    be the result of a typo.
+    """
+    if is_object_type(type_) or is_interface_type(type_):
+        possible_field_names = list(type_.fields)  # type: ignore
+        return suggestion_list(field_name, possible_field_names)
+    # Otherwise, must be a Union type, which does not define fields.
+    return []
diff --git a/src/graphql/validation/rules/fragments_on_composite_types.py b/src/graphql/validation/rules/fragments_on_composite_types.py
new file mode 100644
index 0000000..8c55651
--- /dev/null
+++ b/src/graphql/validation/rules/fragments_on_composite_types.py
@@ -0,0 +1,51 @@
+from typing import Any
+
+from ...error import GraphQLError
+from ...language import (
+    FragmentDefinitionNode,
+    InlineFragmentNode,
+    print_ast,
+)
+from ...type import is_composite_type
+from ...utilities import type_from_ast
+from . import ValidationRule
+
+__all__ = ["FragmentsOnCompositeTypesRule"]
+
+
+class FragmentsOnCompositeTypesRule(ValidationRule):
+    """Fragments on composite type
+
+    Fragments use a type condition to determine if they apply, since fragments can only
+    be spread into a composite type (object, interface, or union), the type condition
+    must also be a composite type.
+    """
+
+    def enter_inline_fragment(self, node: InlineFragmentNode, *_args: Any) -> None:
+        type_condition = node.type_condition
+        if type_condition:
+            type_ = type_from_ast(self.context.schema, type_condition)
+            if type_ and not is_composite_type(type_):
+                type_str = print_ast(type_condition)
+                self.report_error(
+                    GraphQLError(
+                        "Fragment cannot condition"
+                        f" on non composite type '{type_str}'.",
+                        type_condition,
+                    )
+                )
+
+    def enter_fragment_definition(
+        self, node: FragmentDefinitionNode, *_args: Any
+    ) -> None:
+        type_condition = node.type_condition
+        type_ = type_from_ast(self.context.schema, type_condition)
+        if type_ and not is_composite_type(type_):
+            type_str = print_ast(type_condition)
+            self.report_error(
+                GraphQLError(
+                    f"Fragment '{node.name.value}' cannot condition"
+                    f" on non composite type '{type_str}'.",
+                    type_condition,
+                )
+            )
diff --git a/src/graphql/validation/rules/known_argument_names.py b/src/graphql/validation/rules/known_argument_names.py
new file mode 100644
index 0000000..dabdad8
--- /dev/null
+++ b/src/graphql/validation/rules/known_argument_names.py
@@ -0,0 +1,95 @@
+from typing import cast, Any, Dict, List, Union
+
+from ...error import GraphQLError
+from ...language import (
+    ArgumentNode,
+    DirectiveDefinitionNode,
+    DirectiveNode,
+    SKIP,
+    VisitorAction,
+)
+from ...pyutils import did_you_mean, suggestion_list
+from ...type import specified_directives
+from . import ASTValidationRule, SDLValidationContext, ValidationContext
+
+__all__ = ["KnownArgumentNamesRule", "KnownArgumentNamesOnDirectivesRule"]
+
+
+class KnownArgumentNamesOnDirectivesRule(ASTValidationRule):
+    """Known argument names on directives
+
+    A GraphQL directive is only valid if all supplied arguments are defined.
+
+    For internal use only.
+    """
+
+    context: Union[ValidationContext, SDLValidationContext]
+
+    def __init__(self, context: Union[ValidationContext, SDLValidationContext]):
+        super().__init__(context)
+        directive_args: Dict[str, List[str]] = {}
+
+        schema = context.schema
+        defined_directives = schema.directives if schema else specified_directives
+        for directive in cast(List, defined_directives):
+            directive_args[directive.name] = list(directive.args)
+
+        ast_definitions = context.document.definitions
+        for def_ in ast_definitions:
+            if isinstance(def_, DirectiveDefinitionNode):
+                directive_args[def_.name.value] = [
+                    arg.name.value for arg in def_.arguments or []
+                ]
+
+        self.directive_args = directive_args
+
+    def enter_directive(
+        self, directive_node: DirectiveNode, *_args: Any
+    ) -> VisitorAction:
+        directive_name = directive_node.name.value
+        known_args = self.directive_args.get(directive_name)
+        if directive_node.arguments and known_args is not None:
+            for arg_node in directive_node.arguments:
+                arg_name = arg_node.name.value
+                if arg_name not in known_args:
+                    suggestions = suggestion_list(arg_name, known_args)
+                    self.report_error(
+                        GraphQLError(
+                            f"Unknown argument '{arg_name}'"
+                            f" on directive '@{directive_name}'."
+                            + did_you_mean(suggestions),
+                            arg_node,
+                        )
+                    )
+        return SKIP
+
+
+class KnownArgumentNamesRule(KnownArgumentNamesOnDirectivesRule):
+    """Known argument names
+
+    A GraphQL field is only valid if all supplied arguments are defined by that field.
+    """
+
+    context: ValidationContext
+
+    def __init__(self, context: ValidationContext):
+        super().__init__(context)
+
+    def enter_argument(self, arg_node: ArgumentNode, *args: Any) -> None:
+        context = self.context
+        arg_def = context.get_argument()
+        field_def = context.get_field_def()
+        parent_type = context.get_parent_type()
+        if not arg_def and field_def and parent_type:
+            arg_name = arg_node.name.value
+            field_name = args[3][-1].name.value
+            known_args_names = list(field_def.args)
+            suggestions = suggestion_list(arg_name, known_args_names)
+            context.report_error(
+                GraphQLError(
+                    f"Unknown argument '{arg_name}'"
+                    f" on field '{parent_type.name}.{field_name}'."
+                    + did_you_mean(suggestions),
+                    arg_node,
+                )
+            )
diff --git a/src/graphql/validation/rules/known_directives.py b/src/graphql/validation/rules/known_directives.py
new file mode 100644
index 0000000..d6a9e24
--- /dev/null
+++ b/src/graphql/validation/rules/known_directives.py
@@ -0,0 +1,117 @@
+from typing import cast, Any, Dict, List, Optional, Union
+
+from ...error import GraphQLError
+from ...language import (
+    DirectiveLocation,
+    DirectiveDefinitionNode,
+    DirectiveNode,
+    Node,
+    OperationDefinitionNode,
+)
+from ...type import specified_directives
+from . import ASTValidationRule, SDLValidationContext, ValidationContext
+
+__all__ = ["KnownDirectivesRule"]
+
+
+class KnownDirectivesRule(ASTValidationRule):
+    """Known directives
+
+    A GraphQL document is only valid if all ``@directives`` are known by the schema and
+    legally positioned.
+    """
+
+    context: Union[ValidationContext, SDLValidationContext]
+
+    def __init__(self, context: Union[ValidationContext, SDLValidationContext]):
+        super().__init__(context)
+        locations_map: Dict[str, List[DirectiveLocation]] = {}
+
+        schema = context.schema
+        defined_directives = (
+            schema.directives if schema else cast(List, specified_directives)
+        )
+        for directive in defined_directives:
+            locations_map[directive.name] = directive.locations
+        ast_definitions = context.document.definitions
+        for def_ in ast_definitions:
+            if isinstance(def_, DirectiveDefinitionNode):
+                locations_map[def_.name.value] = [
+                    DirectiveLocation[name.value] for name in def_.locations
+                ]
+        self.locations_map = locations_map
+
+    def enter_directive(
+        self,
+        node: DirectiveNode,
+        _key: Any,
+        _parent: Any,
+        _path: Any,
+        ancestors: List[Node],
+    ) -> None:
+        name = node.name.value
+        locations = self.locations_map.get(name)
+        if locations:
+            candidate_location = get_directive_location_for_ast_path(ancestors)
+            if candidate_location and candidate_location not in locations:
+                self.report_error(
+                    GraphQLError(
+                        f"Directive '@{name}'"
+                        f" may not be used on {candidate_location.value}.",
+                        node,
+                    )
+                )
+        else:
+            self.report_error(GraphQLError(f"Unknown directive '@{name}'.", node))
+
+
+_operation_location = {
+    "query": DirectiveLocation.QUERY,
+    "mutation": DirectiveLocation.MUTATION,
+    "subscription": DirectiveLocation.SUBSCRIPTION,
+}
+
+_directive_location = {
+    "field": DirectiveLocation.FIELD,
+    "fragment_spread": DirectiveLocation.FRAGMENT_SPREAD,
+    "inline_fragment": DirectiveLocation.INLINE_FRAGMENT,
+    "fragment_definition": DirectiveLocation.FRAGMENT_DEFINITION,
+    "variable_definition": DirectiveLocation.VARIABLE_DEFINITION,
+    "schema_definition": DirectiveLocation.SCHEMA,
+    "schema_extension": DirectiveLocation.SCHEMA,
+    "scalar_type_definition": DirectiveLocation.SCALAR,
+    "scalar_type_extension": DirectiveLocation.SCALAR,
+    "object_type_definition": DirectiveLocation.OBJECT,
+    "object_type_extension": DirectiveLocation.OBJECT,
+    "field_definition": DirectiveLocation.FIELD_DEFINITION,
+    "interface_type_definition": DirectiveLocation.INTERFACE,
+    "interface_type_extension": DirectiveLocation.INTERFACE,
+    "union_type_definition": DirectiveLocation.UNION,
+    "union_type_extension": DirectiveLocation.UNION,
+    "enum_type_definition": DirectiveLocation.ENUM,
+    "enum_type_extension": DirectiveLocation.ENUM,
+    "enum_value_definition": DirectiveLocation.ENUM_VALUE,
+    "input_object_type_definition": DirectiveLocation.INPUT_OBJECT,
+    "input_object_type_extension": DirectiveLocation.INPUT_OBJECT,
+}
+
+
+def get_directive_location_for_ast_path(
+    ancestors: List[Node],
+) -> Optional[DirectiveLocation]:
+    applied_to = ancestors[-1]
+    if not isinstance(applied_to, Node):  # pragma: no cover
+        raise TypeError("Unexpected error in directive.")
+    kind = applied_to.kind
+    if kind == "operation_definition":
+        applied_to = cast(OperationDefinitionNode, applied_to)
+        return _operation_location[applied_to.operation.value]
+    elif kind == "input_value_definition":
+        parent_node = ancestors[-3]
+        return (
+            DirectiveLocation.INPUT_FIELD_DEFINITION
+            if parent_node.kind == "input_object_type_definition"
+            else DirectiveLocation.ARGUMENT_DEFINITION
+        )
+    else:
+        return _directive_location.get(kind)
diff --git a/src/graphql/validation/rules/known_fragment_names.py b/src/graphql/validation/rules/known_fragment_names.py
new file mode 100644
index 0000000..11ac343
--- /dev/null
+++ b/src/graphql/validation/rules/known_fragment_names.py
@@ -0,0 +1,23 @@
+from typing import Any
+
+from ...error import GraphQLError
+from ...language import FragmentSpreadNode
+from . import ValidationRule
+
+__all__ = ["KnownFragmentNamesRule"]
+
+
+class KnownFragmentNamesRule(ValidationRule):
+    """Known fragment names
+
+    A GraphQL document is only valid if all ``...Fragment`` fragment spreads refer to
+    fragments defined in the same document.
+    """
+
+    def enter_fragment_spread(self, node: FragmentSpreadNode, *_args: Any) -> None:
+        fragment_name = node.name.value
+        fragment = self.context.get_fragment(fragment_name)
+        if not fragment:
+            self.report_error(
+                GraphQLError(f"Unknown fragment '{fragment_name}'.", node.name)
+            )
diff --git a/src/graphql/validation/rules/known_type_names.py b/src/graphql/validation/rules/known_type_names.py
new file mode 100644
index 0000000..c864ef4
--- /dev/null
+++ b/src/graphql/validation/rules/known_type_names.py
@@ -0,0 +1,83 @@
+from typing import Any, Collection, List, Union, cast
+
+from ...error import GraphQLError
+from ...language import (
+    is_type_definition_node,
+    is_type_system_definition_node,
+    is_type_system_extension_node,
+    Node,
+    NamedTypeNode,
+    TypeDefinitionNode,
+)
+from ...type import specified_scalar_types
+from ...pyutils import did_you_mean, suggestion_list
+from . import ASTValidationRule, ValidationContext, SDLValidationContext
+
+__all__ = ["KnownTypeNamesRule"]
+
+
+class KnownTypeNamesRule(ASTValidationRule):
+    """Known type names
+
+    A GraphQL document is only valid if referenced types (specifically variable
+    definitions and fragment conditions) are defined by the type schema.
+    """
+
+    def __init__(self, context: Union[ValidationContext, SDLValidationContext]):
+        super().__init__(context)
+        schema = context.schema
+        self.existing_types_map = schema.type_map if schema else {}
+
+        defined_types = []
+        for def_ in context.document.definitions:
+            if is_type_definition_node(def_):
+                def_ = cast(TypeDefinitionNode, def_)
+                defined_types.append(def_.name.value)
+        self.defined_types = set(defined_types)
+
+        self.type_names = list(self.existing_types_map) + defined_types
+
+    def enter_named_type(
+        self,
+        node: NamedTypeNode,
+        _key: Any,
+        parent: Node,
+        _path: Any,
+        ancestors: List[Node],
+    ) -> None:
+        type_name = node.name.value
+        if (
+            type_name not in self.existing_types_map
+            and type_name not in self.defined_types
+        ):
+            try:
+                definition_node = ancestors[2]
+            except IndexError:
+                definition_node = parent
+            is_sdl = is_sdl_node(definition_node)
+            if is_sdl and type_name in specified_scalar_types:
+                return
+
+            suggested_types = suggestion_list(
+                type_name,
+                list(specified_scalar_types) + self.type_names
+                if is_sdl
+                else self.type_names,
+            )
+            self.report_error(
+                GraphQLError(
+                    f"Unknown type '{type_name}'." + did_you_mean(suggested_types),
+                    node,
+                )
+            )
+
+
+def is_sdl_node(value: Union[Node, Collection[Node], None]) -> bool:
+    return (
+        value is not None
+        and not isinstance(value, list)
+        and (
+            is_type_system_definition_node(cast(Node, value))
+            or is_type_system_extension_node(cast(Node, value))
+        )
+    )
diff --git a/src/graphql/validation/rules/lone_anonymous_operation.py b/src/graphql/validation/rules/lone_anonymous_operation.py
new file mode 100644
index 0000000..3e945f8
--- /dev/null
+++ b/src/graphql/validation/rules/lone_anonymous_operation.py
@@ -0,0 +1,36 @@
+from typing import Any
+
+from ...error import GraphQLError
+from ...language import DocumentNode, OperationDefinitionNode
+from . import ASTValidationContext, ASTValidationRule
+
+__all__ = ["LoneAnonymousOperationRule"]
+
+
+class LoneAnonymousOperationRule(ASTValidationRule):
+    """Lone anonymous operation
+
+    A GraphQL document is only valid if when it contains an anonymous operation
+    (the query short-hand) that it contains only that one operation definition.
+    """
+
+    def __init__(self, context: ASTValidationContext):
+        super().__init__(context)
+        self.operation_count = 0
+
+    def enter_document(self, node: DocumentNode, *_args: Any) -> None:
+        self.operation_count = sum(
+            1
+            for definition in node.definitions
+            if isinstance(definition, OperationDefinitionNode)
+        )
+
+    def enter_operation_definition(
+        self, node: OperationDefinitionNode, *_args: Any
+    ) -> None:
+        if not node.name and self.operation_count > 1:
+            self.report_error(
+                GraphQLError(
+                    "This anonymous operation must be the only defined operation.", node
+                )
+            )
diff --git a/src/graphql/validation/rules/lone_schema_definition.py b/src/graphql/validation/rules/lone_schema_definition.py
new file mode 100644
index 0000000..2d33cb8
--- /dev/null
+++ b/src/graphql/validation/rules/lone_schema_definition.py
@@ -0,0 +1,39 @@
+from typing import Any
+
+from ...error import GraphQLError
+from ...language import SchemaDefinitionNode
+from . import SDLValidationRule, SDLValidationContext
+
+__all__ = ["LoneSchemaDefinitionRule"]
+
+
+class LoneSchemaDefinitionRule(SDLValidationRule):
+    """Lone Schema definition
+
+    A GraphQL document is only valid if it contains only one schema definition.
+    """
+
+    def __init__(self, context: SDLValidationContext):
+        super().__init__(context)
+        old_schema = context.schema
+        self.already_defined = old_schema and (
+            old_schema.ast_node
+            or old_schema.query_type
+            or old_schema.mutation_type
+            or old_schema.subscription_type
+        )
+        self.schema_definitions_count = 0
+
+    def enter_schema_definition(self, node: SchemaDefinitionNode, *_args: Any) -> None:
+        if self.already_defined:
+            self.report_error(
+                GraphQLError(
+                    "Cannot define a new schema within a schema extension.", node
+                )
+            )
+        else:
+            if self.schema_definitions_count:
+                self.report_error(
+                    GraphQLError("Must provide only one schema definition.", node)
+                )
+            self.schema_definitions_count += 1
diff --git a/src/graphql/validation/rules/no_fragment_cycles.py b/src/graphql/validation/rules/no_fragment_cycles.py
new file mode 100644
index 0000000..33959cb
--- /dev/null
+++ b/src/graphql/validation/rules/no_fragment_cycles.py
@@ -0,0 +1,74 @@
+from typing import Any, Dict, List, Set
+
+from ...error import GraphQLError
+from ...language import FragmentDefinitionNode, FragmentSpreadNode, VisitorAction, SKIP
+from . import ASTValidationContext, ASTValidationRule
+
+__all__ = ["NoFragmentCyclesRule"]
+
+
+class NoFragmentCyclesRule(ASTValidationRule):
+    """No fragment cycles"""
+
+    def __init__(self, context: ASTValidationContext):
+        super().__init__(context)
+        # Tracks already visited fragments to maintain O(N) and to ensure that
+        # cycles are not redundantly reported.
+        self.visited_frags: Set[str] = set()
+        # List of AST nodes used to produce meaningful errors
+        self.spread_path: List[FragmentSpreadNode] = []
+        # Position in the spread path
+        self.spread_path_index_by_name: Dict[str, int] = {}
+
+    @staticmethod
+    def enter_operation_definition(*_args: Any) -> VisitorAction:
+        return SKIP
+
+    def enter_fragment_definition(
+        self, node: FragmentDefinitionNode, *_args: Any
+    ) -> VisitorAction:
+        self.detect_cycle_recursive(node)
+        return SKIP
+
+    def detect_cycle_recursive(self, fragment: FragmentDefinitionNode) -> None:
+        # This does a straight-forward DFS to find cycles.
+        # It does not terminate when a cycle was found but continues to explore
+        # the graph to find all possible cycles.
+        if fragment.name.value in self.visited_frags:
+            return
+
+        fragment_name = fragment.name.value
+        visited_frags = self.visited_frags
+        visited_frags.add(fragment_name)
+
+        spread_nodes = self.context.get_fragment_spreads(fragment.selection_set)
+        if not spread_nodes:
+            return
+
+        spread_path = self.spread_path
+        spread_path_index = self.spread_path_index_by_name
+        spread_path_index[fragment_name] = len(spread_path)
+        get_fragment = self.context.get_fragment
+
+        for spread_node in spread_nodes:
+            spread_name = spread_node.name.value
+            cycle_index = spread_path_index.get(spread_name)
+
+            spread_path.append(spread_node)
+            if cycle_index is None:
+                spread_fragment = get_fragment(spread_name)
+                if spread_fragment:
+                    self.detect_cycle_recursive(spread_fragment)
+            else:
+                cycle_path = spread_path[cycle_index:]
+                via_path = ", ".join("'" + s.name.value + "'" for s in cycle_path[:-1])
+                self.report_error(
+                    GraphQLError(
+                        f"Cannot spread fragment '{spread_name}' within itself"
+                        + (f" via {via_path}." if via_path else "."),
+                        cycle_path,
+                    )
+                )
+            spread_path.pop()
+
+        del spread_path_index[fragment_name]
diff --git a/src/graphql/validation/rules/no_undefined_variables.py b/src/graphql/validation/rules/no_undefined_variables.py
new file mode 100644
index 0000000..154b602
--- /dev/null
+++ b/src/graphql/validation/rules/no_undefined_variables.py
@@ -0,0 +1,46 @@
+from typing import Any, Set
+
+from ...error import GraphQLError
+from ...language import OperationDefinitionNode, VariableDefinitionNode
+from . import ValidationContext, ValidationRule
+
+__all__ = ["NoUndefinedVariablesRule"]
+
+
+class NoUndefinedVariablesRule(ValidationRule):
+    """No undefined variables
+
+    A GraphQL operation is only valid if all variables encountered, both directly and
+    via fragment spreads, are defined by that operation.
+    """
+
+    def __init__(self, context: ValidationContext):
+        super().__init__(context)
+        self.defined_variable_names: Set[str] = set()
+
+    def enter_operation_definition(self, *_args: Any) -> None:
+        self.defined_variable_names.clear()
+
+    def leave_operation_definition(
+        self, operation: OperationDefinitionNode, *_args: Any
+    ) -> None:
+        usages = self.context.get_recursive_variable_usages(operation)
+        defined_variables = self.defined_variable_names
+        for usage in usages:
+            node = usage.node
+            var_name = node.name.value
+            if var_name not in defined_variables:
+                self.report_error(
+                    GraphQLError(
+                        f"Variable '${var_name}' is not defined"
+                        f" by operation '{operation.name.value}'."
+                        if operation.name
+                        else f"Variable '${var_name}' is not defined.",
+                        [node, operation],
+                    )
+                )
+
+    def enter_variable_definition(
+        self, node: VariableDefinitionNode, *_args: Any
+    ) -> None:
+        self.defined_variable_names.add(node.variable.name.value)
diff --git a/src/graphql/validation/rules/no_unused_fragments.py b/src/graphql/validation/rules/no_unused_fragments.py
new file mode 100644
index 0000000..38209a1
--- /dev/null
+++ b/src/graphql/validation/rules/no_unused_fragments.py
@@ -0,0 +1,51 @@
+from typing import Any, List
+
+from ...error import GraphQLError
+from ...language import (
+    FragmentDefinitionNode,
+    OperationDefinitionNode,
+    VisitorAction,
+    SKIP,
+)
+from . import ASTValidationContext, ASTValidationRule
+
+__all__ = ["NoUnusedFragmentsRule"]
+
+
+class NoUnusedFragmentsRule(ASTValidationRule):
+    """No unused fragments
+
+    A GraphQL document is only valid if all fragment definitions are spread within
+    operations, or spread within other fragments spread within operations.
+    """
+
+    def __init__(self, context: ASTValidationContext):
+        super().__init__(context)
+        self.operation_defs: List[OperationDefinitionNode] = []
+        self.fragment_defs: List[FragmentDefinitionNode] = []
+
+    def enter_operation_definition(
+        self, node: OperationDefinitionNode, *_args: Any
+    ) -> VisitorAction:
+        self.operation_defs.append(node)
+        return SKIP
+
+    def enter_fragment_definition(
+        self, node: FragmentDefinitionNode, *_args: Any
+    ) -> VisitorAction:
+        self.fragment_defs.append(node)
+        return SKIP
+
+    def leave_document(self, *_args: Any) -> None:
+        fragment_names_used = set()
+        get_fragments = self.context.get_recursively_referenced_fragments
+        for operation in self.operation_defs:
+            for fragment in get_fragments(operation):
+                fragment_names_used.add(fragment.name.value)
+
+        for fragment_def in self.fragment_defs:
+            frag_name = fragment_def.name.value
+            if frag_name not in fragment_names_used:
+                self.report_error(
+                    GraphQLError(f"Fragment '{frag_name}' is never used.", fragment_def)
+                )
diff --git a/src/graphql/validation/rules/no_unused_variables.py b/src/graphql/validation/rules/no_unused_variables.py
new file mode 100644
index 0000000..d0247ff
--- /dev/null
+++ b/src/graphql/validation/rules/no_unused_variables.py
@@ -0,0 +1,49 @@
+from typing import Any, List, Set
+
+from ...error import GraphQLError
+from ...language import OperationDefinitionNode, VariableDefinitionNode
+from . import ValidationContext, ValidationRule
+
+__all__ = ["NoUnusedVariablesRule"]
+
+
+class NoUnusedVariablesRule(ValidationRule):
+    """No unused variables
+
+    A GraphQL operation is only valid if all variables defined by an operation are used,
+    either directly or within a spread fragment.
+    """
+
+    def __init__(self, context: ValidationContext):
+        super().__init__(context)
+        self.variable_defs: List[VariableDefinitionNode] = []
+
+    def enter_operation_definition(self, *_args: Any) -> None:
+        self.variable_defs.clear()
+
+    def leave_operation_definition(
+        self, operation: OperationDefinitionNode, *_args: Any
+    ) -> None:
+        variable_name_used: Set[str] = set()
+        usages = self.context.get_recursive_variable_usages(operation)
+
+        for usage in usages:
+            variable_name_used.add(usage.node.name.value)
+
+        for variable_def in self.variable_defs:
+            variable_name = variable_def.variable.name.value
+            if variable_name not in variable_name_used:
+                self.report_error(
+                    GraphQLError(
+                        f"Variable '${variable_name}' is never used"
+                        f" in operation '{operation.name.value}'."
+                        if operation.name
+                        else f"Variable '${variable_name}' is never used.",
+                        variable_def,
+                    )
+                )
+
+    def enter_variable_definition(
+        self, definition: VariableDefinitionNode, *_args: Any
+    ) -> None:
+        self.variable_defs.append(definition)
diff --git a/src/graphql/validation/rules/overlapping_fields_can_be_merged.py b/src/graphql/validation/rules/overlapping_fields_can_be_merged.py
new file mode 100644
index 0000000..dcf7893
--- /dev/null
+++ b/src/graphql/validation/rules/overlapping_fields_can_be_merged.py
@@ -0,0 +1,759 @@
+from itertools import chain
+from typing import Any, Collection, Dict, List, Optional, Tuple, Union, cast
+
+from ...error import GraphQLError
+from ...language import (
+    ArgumentNode,
+    FieldNode,
+    FragmentDefinitionNode,
+    FragmentSpreadNode,
+    InlineFragmentNode,
+    SelectionSetNode,
+    ValueNode,
+    print_ast,
+)
+from ...type import (
+    GraphQLCompositeType,
+    GraphQLField,
+    GraphQLList,
+    GraphQLNamedType,
+    GraphQLNonNull,
+    GraphQLOutputType,
+    get_named_type,
+    is_interface_type,
+    is_leaf_type,
+    is_list_type,
+    is_non_null_type,
+    is_object_type,
+)
+from ...utilities import type_from_ast
+from . import ValidationContext, ValidationRule
+
+MYPY = False
+
+__all__ = ["OverlappingFieldsCanBeMergedRule"]
+
+
+def reason_message(reason: "ConflictReasonMessage") -> str:
+    if isinstance(reason, list):
+        return " and ".join(
+            f"subfields '{response_name}' conflict"
+            f" because {reason_message(sub_reason)}"
+            for response_name, sub_reason in reason
+        )
+    return reason
+
+
+class OverlappingFieldsCanBeMergedRule(ValidationRule):
+    """Overlapping fields can be merged
+
+    A selection set is only valid if all fields (including spreading any fragments)
+    either correspond to distinct response names or can be merged without ambiguity.
+    """
+
+    def __init__(self, context: ValidationContext):
+        super().__init__(context)
+        # A memoization for when two fragments are compared "between" each other for
+        # conflicts. Two fragments may be compared many times, so memoizing this can
+        # dramatically improve the performance of this validator.
+        self.compared_fragment_pairs = PairSet()
+
+        # A cache for the "field map" and list of fragment names found in any given
+        # selection set. Selection sets may be asked for this information multiple
+        # times, so this improves the performance of this validator.
+        self.cached_fields_and_fragment_names: Dict = {}
+
+    def enter_selection_set(self, selection_set: SelectionSetNode, *_args: Any) -> None:
+        conflicts = find_conflicts_within_selection_set(
+            self.context,
+            self.cached_fields_and_fragment_names,
+            self.compared_fragment_pairs,
+            self.context.get_parent_type(),
+            selection_set,
+        )
+        for (reason_name, reason), fields1, fields2 in conflicts:
+            reason_msg = reason_message(reason)
+            self.report_error(
+                GraphQLError(
+                    f"Fields '{reason_name}' conflict because {reason_msg}."
+                    " Use different aliases on the fields to fetch both"
+                    " if this was intentional.",
+                    fields1 + fields2,
+                )
+            )
+
+
+Conflict = Tuple["ConflictReason", List[FieldNode], List[FieldNode]]
+# Field name and reason.
+ConflictReason = Tuple[str, "ConflictReasonMessage"]
+# Reason is a string, or a nested list of conflicts.
+if MYPY:  # recursive types not fully supported yet (/python/mypy/issues/731)
+    ConflictReasonMessage = Union[str, List]
+else:
+    ConflictReasonMessage = Union[str, List[ConflictReason]]
+# Tuple defining a field node in a context.
+NodeAndDef = Tuple[GraphQLCompositeType, FieldNode, Optional[GraphQLField]]
+# Dictionary of lists of those.
+NodeAndDefCollection = Dict[str, List[NodeAndDef]]
+
+
+# Algorithm:
+#
+# Conflicts occur when two fields exist in a query which will produce the same
+# response name, but represent differing values, thus creating a conflict.
+# The algorithm below finds all conflicts via making a series of comparisons
+# between fields. In order to compare as few fields as possible, this makes
+# a series of comparisons "within" sets of fields and "between" sets of fields.
+#
+# Given any selection set, a collection produces both a set of fields by
+# also including all inline fragments, as well as a list of fragments
+# referenced by fragment spreads.
+#
+# A) Each selection set represented in the document first compares "within" its
+# collected set of fields, finding any conflicts between every pair of
+# overlapping fields.
+# Note: This is the#only time* that a the fields "within" a set are compared
+# to each other. After this only fields "between" sets are compared.
+#
+# B) Also, if any fragment is referenced in a selection set, then a
+# comparison is made "between" the original set of fields and the
+# referenced fragment.
+#
+# C) Also, if multiple fragments are referenced, then comparisons
+# are made "between" each referenced fragment.
+#
+# D) When comparing "between" a set of fields and a referenced fragment, first
+# a comparison is made between each field in the original set of fields and
+# each field in the the referenced set of fields.
+#
+# E) Also, if any fragment is referenced in the referenced selection set,
+# then a comparison is made "between" the original set of fields and the
+# referenced fragment (recursively referring to step D).
+#
+# F) When comparing "between" two fragments, first a comparison is made between
+# each field in the first referenced set of fields and each field in the the
+# second referenced set of fields.
+#
+# G) Also, any fragments referenced by the first must be compared to the
+# second, and any fragments referenced by the second must be compared to the
+# first (recursively referring to step F).
+#
+# H) When comparing two fields, if both have selection sets, then a comparison
+# is made "between" both selection sets, first comparing the set of fields in
+# the first selection set with the set of fields in the second.
+#
+# I) Also, if any fragment is referenced in either selection set, then a
+# comparison is made "between" the other set of fields and the
+# referenced fragment.
+#
+# J) Also, if two fragments are referenced in both selection sets, then a
+# comparison is made "between" the two fragments.
+
+
+def find_conflicts_within_selection_set(
+    context: ValidationContext,
+    cached_fields_and_fragment_names: Dict,
+    compared_fragment_pairs: "PairSet",
+    parent_type: Optional[GraphQLNamedType],
+    selection_set: SelectionSetNode,
+) -> List[Conflict]:
+    """Find conflicts within selection set.
+
+    Find all conflicts found "within" a selection set, including those found via
+    spreading in fragments.
+
+    Called when visiting each SelectionSet in the GraphQL Document.
+    """
+    conflicts: List[Conflict] = []
+
+    field_map, fragment_names = get_fields_and_fragment_names(
+        context, cached_fields_and_fragment_names, parent_type, selection_set
+    )
+
+    # (A) Find all conflicts "within" the fields of this selection set.
+    # Note: this is the *only place* `collect_conflicts_within` is called.
+    collect_conflicts_within(
+        context,
+        conflicts,
+        cached_fields_and_fragment_names,
+        compared_fragment_pairs,
+        field_map,
+    )
+
+    if fragment_names:
+        # (B) Then collect conflicts between these fields and those represented by each
+        # spread fragment name found.
+        for i, fragment_name in enumerate(fragment_names):
+            collect_conflicts_between_fields_and_fragment(
+                context,
+                conflicts,
+                cached_fields_and_fragment_names,
+                compared_fragment_pairs,
+                False,
+                field_map,
+                fragment_name,
+            )
+            # (C) Then compare this fragment with all other fragments found in this
+            # selection set to collect conflicts within fragments spread together.
+            # This compares each item in the list of fragment names to every other
+            # item in that same list (except for itself).
+            for other_fragment_name in fragment_names[i + 1 :]:
+                collect_conflicts_between_fragments(
+                    context,
+                    conflicts,
+                    cached_fields_and_fragment_names,
+                    compared_fragment_pairs,
+                    False,
+                    fragment_name,
+                    other_fragment_name,
+                )
+
+    return conflicts
+
+
+def collect_conflicts_between_fields_and_fragment(
+    context: ValidationContext,
+    conflicts: List[Conflict],
+    cached_fields_and_fragment_names: Dict,
+    compared_fragment_pairs: "PairSet",
+    are_mutually_exclusive: bool,
+    field_map: NodeAndDefCollection,
+    fragment_name: str,
+) -> None:
+    """Collect conflicts between fields and fragment.
+
+    Collect all conflicts found between a set of fields and a fragment reference
+    including via spreading in any nested fragments.
+    """
+    fragment = context.get_fragment(fragment_name)
+    if not fragment:
+        return None
+
+    field_map2, fragment_names2 = get_referenced_fields_and_fragment_names(
+        context, cached_fields_and_fragment_names, fragment
+    )
+
+    # Do not compare a fragment's fieldMap to itself.
+    if field_map is field_map2:
+        return
+
+    # (D) First collect any conflicts between the provided collection of fields and the
+    # collection of fields represented by the given fragment.
+    collect_conflicts_between(
+        context,
+        conflicts,
+        cached_fields_and_fragment_names,
+        compared_fragment_pairs,
+        are_mutually_exclusive,
+        field_map,
+        field_map2,
+    )
+
+    # (E) Then collect any conflicts between the provided collection of fields and any
+    # fragment names found in the given fragment.
+    for fragment_name2 in fragment_names2:
+        collect_conflicts_between_fields_and_fragment(
+            context,
+            conflicts,
+            cached_fields_and_fragment_names,
+            compared_fragment_pairs,
+            are_mutually_exclusive,
+            field_map,
+            fragment_name2,
+        )
+
+
+def collect_conflicts_between_fragments(
+    context: ValidationContext,
+    conflicts: List[Conflict],
+    cached_fields_and_fragment_names: Dict,
+    compared_fragment_pairs: "PairSet",
+    are_mutually_exclusive: bool,
+    fragment_name1: str,
+    fragment_name2: str,
+) -> None:
+    """Collect conflicts between fragments.
+
+    Collect all conflicts found between two fragments, including via spreading in any
+    nested fragments.
+    """
+    # No need to compare a fragment to itself.
+    if fragment_name1 == fragment_name2:
+        return
+
+    # Memoize so two fragments are not compared for conflicts more than once.
+    if compared_fragment_pairs.has(
+        fragment_name1, fragment_name2, are_mutually_exclusive
+    ):
+        return
+    compared_fragment_pairs.add(fragment_name1, fragment_name2, are_mutually_exclusive)
+
+    fragment1 = context.get_fragment(fragment_name1)
+    fragment2 = context.get_fragment(fragment_name2)
+    if not fragment1 or not fragment2:
+        return None
+
+    field_map1, fragment_names1 = get_referenced_fields_and_fragment_names(
+        context, cached_fields_and_fragment_names, fragment1
+    )
+
+    field_map2, fragment_names2 = get_referenced_fields_and_fragment_names(
+        context, cached_fields_and_fragment_names, fragment2
+    )
+
+    # (F) First, collect all conflicts between these two collections of fields
+    # (not including any nested fragments)
+    collect_conflicts_between(
+        context,
+        conflicts,
+        cached_fields_and_fragment_names,
+        compared_fragment_pairs,
+        are_mutually_exclusive,
+        field_map1,
+        field_map2,
+    )
+
+    # (G) Then collect conflicts between the first fragment and any nested fragments
+    # spread in the second fragment.
+    for nested_fragment_name2 in fragment_names2:
+        collect_conflicts_between_fragments(
+            context,
+            conflicts,
+            cached_fields_and_fragment_names,
+            compared_fragment_pairs,
+            are_mutually_exclusive,
+            fragment_name1,
+            nested_fragment_name2,
+        )
+
+    # (G) Then collect conflicts between the second fragment and any nested fragments
+    # spread in the first fragment.
+    for nested_fragment_name1 in fragment_names1:
+        collect_conflicts_between_fragments(
+            context,
+            conflicts,
+            cached_fields_and_fragment_names,
+            compared_fragment_pairs,
+            are_mutually_exclusive,
+            nested_fragment_name1,
+            fragment_name2,
+        )
+
+
+def find_conflicts_between_sub_selection_sets(
+    context: ValidationContext,
+    cached_fields_and_fragment_names: Dict,
+    compared_fragment_pairs: "PairSet",
+    are_mutually_exclusive: bool,
+    parent_type1: Optional[GraphQLNamedType],
+    selection_set1: SelectionSetNode,
+    parent_type2: Optional[GraphQLNamedType],
+    selection_set2: SelectionSetNode,
+) -> List[Conflict]:
+    """Find conflicts between sub selection sets.
+
+    Find all conflicts found between two selection sets, including those found via
+    spreading in fragments. Called when determining if conflicts exist between the
+    sub-fields of two overlapping fields.
+    """
+    conflicts: List[Conflict] = []
+
+    field_map1, fragment_names1 = get_fields_and_fragment_names(
+        context, cached_fields_and_fragment_names, parent_type1, selection_set1
+    )
+    field_map2, fragment_names2 = get_fields_and_fragment_names(
+        context, cached_fields_and_fragment_names, parent_type2, selection_set2
+    )
+
+    # (H) First, collect all conflicts between these two collections of field.
+    collect_conflicts_between(
+        context,
+        conflicts,
+        cached_fields_and_fragment_names,
+        compared_fragment_pairs,
+        are_mutually_exclusive,
+        field_map1,
+        field_map2,
+    )
+
+    # (I) Then collect conflicts between the first collection of fields and those
+    # referenced by each fragment name associated with the second.
+    if fragment_names2:
+        for fragment_name2 in fragment_names2:
+            collect_conflicts_between_fields_and_fragment(
+                context,
+                conflicts,
+                cached_fields_and_fragment_names,
+                compared_fragment_pairs,
+                are_mutually_exclusive,
+                field_map1,
+                fragment_name2,
+            )
+
+    # (I) Then collect conflicts between the second collection of fields and those
+    # referenced by each fragment name associated with the first.
+    if fragment_names1:
+        for fragment_name1 in fragment_names1:
+            collect_conflicts_between_fields_and_fragment(
+                context,
+                conflicts,
+                cached_fields_and_fragment_names,
+                compared_fragment_pairs,
+                are_mutually_exclusive,
+                field_map2,
+                fragment_name1,
+            )
+
+    # (J) Also collect conflicts between any fragment names by the first and fragment
+    # names by the second. This compares each item in the first set of names to each
+    # item in the second set of names.
+    for fragment_name1 in fragment_names1:
+        for fragment_name2 in fragment_names2:
+            collect_conflicts_between_fragments(
+                context,
+                conflicts,
+                cached_fields_and_fragment_names,
+                compared_fragment_pairs,
+                are_mutually_exclusive,
+                fragment_name1,
+                fragment_name2,
+            )
+
+    return conflicts
+
+
+def collect_conflicts_within(
+    context: ValidationContext,
+    conflicts: List[Conflict],
+    cached_fields_and_fragment_names: Dict,
+    compared_fragment_pairs: "PairSet",
+    field_map: NodeAndDefCollection,
+) -> None:
+    """Collect all Conflicts "within" one collection of fields."""
+    # A field map is a keyed collection, where each key represents a response name and
+    # the value at that key is a list of all fields which provide that response name.
+    # For every response name, if there are multiple fields, they must be compared to
+    # find a potential conflict.
+    for response_name, fields in field_map.items():
+        # This compares every field in the list to every other field in this list
+        # (except to itself). If the list only has one item, nothing needs to be
+        # compared.
+        if len(fields) > 1:
+            for i, field in enumerate(fields):
+                for other_field in fields[i + 1 :]:
+                    conflict = find_conflict(
+                        context,
+                        cached_fields_and_fragment_names,
+                        compared_fragment_pairs,
+                        # within one collection is never mutually exclusive
+                        False,
+                        response_name,
+                        field,
+                        other_field,
+                    )
+                    if conflict:
+                        conflicts.append(conflict)
+
+
+def collect_conflicts_between(
+    context: ValidationContext,
+    conflicts: List[Conflict],
+    cached_fields_and_fragment_names: Dict,
+    compared_fragment_pairs: "PairSet",
+    parent_fields_are_mutually_exclusive: bool,
+    field_map1: NodeAndDefCollection,
+    field_map2: NodeAndDefCollection,
+) -> None:
+    """Collect all Conflicts between two collections of fields.
+
+    This is similar to, but different from the :func:`~.collect_conflicts_within`
+    function above. This check assumes that :func:`~.collect_conflicts_within` has
+    already been called on each provided collection of fields. This is true because
+    this validator traverses each individual selection set.
+    """
+    # A field map is a keyed collection, where each key represents a response name and
+    # the value at that key is a list of all fields which provide that response name.
+    # For any response name which appears in both provided field maps, each field from
+    # the first field map must be compared to every field in the second field map to
+    # find potential conflicts.
+    for response_name, fields1 in field_map1.items():
+        fields2 = field_map2.get(response_name)
+        if fields2:
+            for field1 in fields1:
+                for field2 in fields2:
+                    conflict = find_conflict(
+                        context,
+                        cached_fields_and_fragment_names,
+                        compared_fragment_pairs,
+                        parent_fields_are_mutually_exclusive,
+                        response_name,
+                        field1,
+                        field2,
+                    )
+                    if conflict:
+                        conflicts.append(conflict)
+
+
+def find_conflict(
+    context: ValidationContext,
+    cached_fields_and_fragment_names: Dict,
+    compared_fragment_pairs: "PairSet",
+    parent_fields_are_mutually_exclusive: bool,
+    response_name: str,
+    field1: NodeAndDef,
+    field2: NodeAndDef,
+) -> Optional[Conflict]:
+    """Find conflict.
+
+    Determines if there is a conflict between two particular fields, including comparing
+    their sub-fields.
+    """
+    parent_type1, node1, def1 = field1
+    parent_type2, node2, def2 = field2
+
+    # If it is known that two fields could not possibly apply at the same time, due to
+    # the parent types, then it is safe to permit them to diverge in aliased field or
+    # arguments used as they will not present any ambiguity by differing. It is known
+    # that two parent types could never overlap if they are different Object types.
+    # Interface or Union types might overlap - if not in the current state of the
+    # schema, then perhaps in some future version, thus may not safely diverge.
+    are_mutually_exclusive = parent_fields_are_mutually_exclusive or (
+        parent_type1 != parent_type2
+        and is_object_type(parent_type1)
+        and is_object_type(parent_type2)
+    )
+
+    # The return type for each field.
+    type1 = cast(Optional[GraphQLOutputType], def1 and def1.type)
+    type2 = cast(Optional[GraphQLOutputType], def2 and def2.type)
+
+    if not are_mutually_exclusive:
+        # Two aliases must refer to the same field.
+        name1 = node1.name.value
+        name2 = node2.name.value
+        if name1 != name2:
+            return (
+                (response_name, f"'{name1}' and '{name2}' are different fields"),
+                [node1],
+                [node2],
+            )
+
+        # Two field calls must have the same arguments.
+        if not same_arguments(node1.arguments or [], node2.arguments or []):
+            return (response_name, "they have differing arguments"), [node1], [node2]
+
+    if type1 and type2 and do_types_conflict(type1, type2):
+        return (
+            (response_name, f"they return conflicting types '{type1}' and '{type2}'"),
+            [node1],
+            [node2],
+        )
+
+    # Collect and compare sub-fields. Use the same "visited fragment names" list for
+    # both collections so fields in a fragment reference are never compared to
+    # themselves.
+    selection_set1 = node1.selection_set
+    selection_set2 = node2.selection_set
+    if selection_set1 and selection_set2:
+        conflicts = find_conflicts_between_sub_selection_sets(
+            context,
+            cached_fields_and_fragment_names,
+            compared_fragment_pairs,
+            are_mutually_exclusive,
+            get_named_type(type1),
+            selection_set1,
+            get_named_type(type2),
+            selection_set2,
+        )
+        return subfield_conflicts(conflicts, response_name, node1, node2)
+
+    return None  # no conflict
+
+
+def same_arguments(
+    arguments1: Collection[ArgumentNode], arguments2: Collection[ArgumentNode]
+) -> bool:
+    if len(arguments1) != len(arguments2):
+        return False
+    for argument1 in arguments1:
+        for argument2 in arguments2:
+            if argument2.name.value == argument1.name.value:
+                if not same_value(argument1.value, argument2.value):
+                    return False
+                break
+        else:
+            return False
+    return True
+
+
+def same_value(value1: ValueNode, value2: ValueNode) -> bool:
+    return print_ast(value1) == print_ast(value2)
+
+
+def do_types_conflict(type1: GraphQLOutputType, type2: GraphQLOutputType) -> bool:
+    """Check whether two types conflict
+
+    Two types conflict if both types could not apply to a value simultaneously.
+    Composite types are ignored as their individual field types will be compared later
+    recursively. However List and Non-Null types must match.
+    """
+    if is_list_type(type1):
+        return (
+            do_types_conflict(
+                cast(GraphQLList, type1).of_type, cast(GraphQLList, type2).of_type
+            )
+            if is_list_type(type2)
+            else True
+        )
+    if is_list_type(type2):
+        return True
+    if is_non_null_type(type1):
+        return (
+            do_types_conflict(
+                cast(GraphQLNonNull, type1).of_type, cast(GraphQLNonNull, type2).of_type
+            )
+            if is_non_null_type(type2)
+            else True
+        )
+    if is_non_null_type(type2):
+        return True
+    if is_leaf_type(type1) or is_leaf_type(type2):
+        return type1 is not type2
+    return False
+
+
+def get_fields_and_fragment_names(
+    context: ValidationContext,
+    cached_fields_and_fragment_names: Dict,
+    parent_type: Optional[GraphQLNamedType],
+    selection_set: SelectionSetNode,
+) -> Tuple[NodeAndDefCollection, List[str]]:
+    """Get fields and referenced fragment names
+
+    Given a selection set, return the collection of fields (a mapping of response name
+    to field nodes and definitions) as well as a list of fragment names referenced via
+    fragment spreads.
+    """
+    cached = cached_fields_and_fragment_names.get(selection_set)
+    if not cached:
+        node_and_defs: NodeAndDefCollection = {}
+        fragment_names: Dict[str, bool] = {}
+        collect_fields_and_fragment_names(
+            context, parent_type, selection_set, node_and_defs, fragment_names
+        )
+        cached = (node_and_defs, list(fragment_names))
+        cached_fields_and_fragment_names[selection_set] = cached
+    return cached
+
+
+def get_referenced_fields_and_fragment_names(
+    context: ValidationContext,
+    cached_fields_and_fragment_names: Dict,
+    fragment: FragmentDefinitionNode,
+) -> Tuple[NodeAndDefCollection, List[str]]:
+    """Get referenced fields and nested fragment names
+
+    Given a reference to a fragment, return the represented collection of fields as well
+    as a list of nested fragment names referenced via fragment spreads.
+    """
+    # Short-circuit building a type from the node if possible.
+    cached = cached_fields_and_fragment_names.get(fragment.selection_set)
+    if cached:
+        return cached
+
+    fragment_type = type_from_ast(context.schema, fragment.type_condition)
+    return get_fields_and_fragment_names(
+        context, cached_fields_and_fragment_names, fragment_type, fragment.selection_set
+    )
+
+
+def collect_fields_and_fragment_names(
+    context: ValidationContext,
+    parent_type: Optional[GraphQLNamedType],
+    selection_set: SelectionSetNode,
+    node_and_defs: NodeAndDefCollection,
+    fragment_names: Dict[str, bool],
+) -> None:
+    for selection in selection_set.selections:
+        if isinstance(selection, FieldNode):
+            field_name = selection.name.value
+            field_def = (
+                parent_type.fields.get(field_name)  # type: ignore
+                if is_object_type(parent_type) or is_interface_type(parent_type)
+                else None
+            )
+            response_name = selection.alias.value if selection.alias else field_name
+            if not node_and_defs.get(response_name):
+                node_and_defs[response_name] = []
+            node_and_defs[response_name].append(
+                cast(NodeAndDef, (parent_type, selection, field_def))
+            )
+        elif isinstance(selection, FragmentSpreadNode):
+            fragment_names[selection.name.value] = True
+        elif isinstance(selection, InlineFragmentNode):  # pragma: no cover else
+            type_condition = selection.type_condition
+            inline_fragment_type = (
+                type_from_ast(context.schema, type_condition)
+                if type_condition
+                else parent_type
+            )
+            collect_fields_and_fragment_names(
+                context,
+                inline_fragment_type,
+                selection.selection_set,
+                node_and_defs,
+                fragment_names,
+            )
+
+
+def subfield_conflicts(
+    conflicts: List[Conflict], response_name: str, node1: FieldNode, node2: FieldNode
+) -> Optional[Conflict]:
+    """Check whether there are conflicts between sub-fields.
+
+    Given a series of Conflicts which occurred between two sub-fields, generate a single
+    Conflict.
+    """
+    if conflicts:
+        return (
+            (response_name, [conflict[0] for conflict in conflicts]),
+            list(chain([node1], *[conflict[1] for conflict in conflicts])),
+            list(chain([node2], *[conflict[2] for conflict in conflicts])),
+        )
+    return None  # no conflict
+
+
+class PairSet:
+    """Pair set
+
+    A way to keep track of pairs of things when the ordering of the pair does not
+    matter. We do this by maintaining a sort of double adjacency sets.
+    """
+
+    __slots__ = ("_data",)
+
+    def __init__(self) -> None:
+        self._data: Dict[str, Dict[str, bool]] = {}
+
+    def has(self, a: str, b: str, are_mutually_exclusive: bool) -> bool:
+        first = self._data.get(a)
+        result = first and first.get(b)
+        if result is None:
+            return False
+        # `are_mutually_exclusive` being False is a superset of being True, hence if we
+        # want to know if this PairSet "has" these two with no exclusivity, we have to
+        # ensure it was added as such.
+        if not are_mutually_exclusive:
+            return not result
+        return True
+
+    def add(self, a: str, b: str, are_mutually_exclusive: bool) -> "PairSet":
+        self._pair_set_add(a, b, are_mutually_exclusive)
+        self._pair_set_add(b, a, are_mutually_exclusive)
+        return self
+
+    def _pair_set_add(self, a: str, b: str, are_mutually_exclusive: bool) -> None:
+        a_map = self._data.get(a)
+        if not a_map:
+            self._data[a] = a_map = {}
+        a_map[b] = are_mutually_exclusive
diff --git a/src/graphql/validation/rules/possible_fragment_spreads.py b/src/graphql/validation/rules/possible_fragment_spreads.py
new file mode 100644
index 0000000..27c2ad9
--- /dev/null
+++ b/src/graphql/validation/rules/possible_fragment_spreads.py
@@ -0,0 +1,66 @@
+from typing import cast, Any, Optional
+
+from ...error import GraphQLError
+from ...language import FragmentSpreadNode, InlineFragmentNode
+from ...type import GraphQLCompositeType, is_composite_type
+from ...utilities import do_types_overlap, type_from_ast
+from . import ValidationRule
+
+__all__ = ["PossibleFragmentSpreadsRule"]
+
+
+class PossibleFragmentSpreadsRule(ValidationRule):
+    """Possible fragment spread
+
+    A fragment spread is only valid if the type condition could ever possibly be true:
+    if there is a non-empty intersection of the possible parent types, and possible
+    types which pass the type condition.
+    """
+
+    def enter_inline_fragment(self, node: InlineFragmentNode, *_args: Any) -> None:
+        context = self.context
+        frag_type = context.get_type()
+        parent_type = context.get_parent_type()
+        if (
+            is_composite_type(frag_type)
+            and is_composite_type(parent_type)
+            and not do_types_overlap(
+                context.schema,
+                cast(GraphQLCompositeType, frag_type),
+                cast(GraphQLCompositeType, parent_type),
+            )
+        ):
+            context.report_error(
+                GraphQLError(
+                    f"Fragment cannot be spread here as objects"
+                    f" of type '{parent_type}' can never be of type '{frag_type}'.",
+                    node,
+                )
+            )
+
+    def enter_fragment_spread(self, node: FragmentSpreadNode, *_args: Any) -> None:
+        context = self.context
+        frag_name = node.name.value
+        frag_type = self.get_fragment_type(frag_name)
+        parent_type = context.get_parent_type()
+        if (
+            frag_type
+            and parent_type
+            and not do_types_overlap(context.schema, frag_type, parent_type)
+        ):
+            context.report_error(
+                GraphQLError(
+                    f"Fragment '{frag_name}' cannot be spread here as objects"
+                    f" of type '{parent_type}' can never be of type '{frag_type}'.",
+                    node,
+                )
+            )
+
+    def get_fragment_type(self, name: str) -> Optional[GraphQLCompositeType]:
+        context = self.context
+        frag = context.get_fragment(name)
+        if frag:
+            type_ = type_from_ast(context.schema, frag.type_condition)
+            if is_composite_type(type_):
+                return cast(GraphQLCompositeType, type_)
+        return None
diff --git a/src/graphql/validation/rules/possible_type_extensions.py b/src/graphql/validation/rules/possible_type_extensions.py
new file mode 100644
index 0000000..d073fc0
--- /dev/null
+++ b/src/graphql/validation/rules/possible_type_extensions.py
@@ -0,0 +1,109 @@
+import re
+from functools import partial
+from typing import Any, Optional
+
+from ...error import GraphQLError
+from ...language import TypeDefinitionNode, TypeExtensionNode
+from ...pyutils import did_you_mean, inspect, suggestion_list
+from ...type import (
+    is_enum_type,
+    is_input_object_type,
+    is_interface_type,
+    is_object_type,
+    is_scalar_type,
+    is_union_type,
+)
+from . import SDLValidationContext, SDLValidationRule
+
+__all__ = ["PossibleTypeExtensionsRule"]
+
+
+class PossibleTypeExtensionsRule(SDLValidationRule):
+    """Possible type extension
+
+     A type extension is only valid if the type is defined and has the same kind.
+    """
+
+    def __init__(self, context: SDLValidationContext):
+        super().__init__(context)
+        self.schema = context.schema
+        self.defined_types = {
+            def_.name.value: def_
+            for def_ in context.document.definitions
+            if isinstance(def_, TypeDefinitionNode)
+        }
+
+    def check_extension(self, node: TypeExtensionNode, *_args: Any) -> None:
+        schema = self.schema
+        type_name = node.name.value
+        def_node = self.defined_types.get(type_name)
+        existing_type = schema.get_type(type_name) if schema else None
+
+        expected_kind: Optional[str]
+        if def_node:
+            expected_kind = def_kind_to_ext_kind(def_node.kind)
+        elif existing_type:
+            expected_kind = type_to_ext_kind(existing_type)
+        else:
+            expected_kind = None
+
+        if expected_kind:
+            if expected_kind != node.kind:
+                kind_str = extension_kind_to_type_name(node.kind)
+                self.report_error(
+                    GraphQLError(
+                        f"Cannot extend non-{kind_str} type '{type_name}'.",
+                        [def_node, node] if def_node else node,
+                    )
+                )
+        else:
+            all_type_names = list(self.defined_types)
+            if self.schema:
+                all_type_names.extend(self.schema.type_map)
+            suggested_types = suggestion_list(type_name, all_type_names)
+            self.report_error(
+                GraphQLError(
+                    f"Cannot extend type '{type_name}' because it is not defined."
+                    + did_you_mean(suggested_types),
+                    node.name,
+                )
+            )
+
+    enter_scalar_type_extension = enter_object_type_extension = check_extension
+    enter_interface_type_extension = enter_union_type_extension = check_extension
+    enter_enum_type_extension = enter_input_object_type_extension = check_extension
+
+
+def_kind_to_ext_kind = partial(re.compile("(?<=_type_)definition$").sub, "extension")
+
+
+def type_to_ext_kind(type_: Any) -> str:
+    if is_scalar_type(type_):
+        return "scalar_type_extension"
+    if is_object_type(type_):
+        return "object_type_extension"
+    if is_interface_type(type_):
+        return "interface_type_extension"
+    if is_union_type(type_):
+        return "union_type_extension"
+    if is_enum_type(type_):
+        return "enum_type_extension"
+    if is_input_object_type(type_):
+        return "input_object_type_extension"
+
+    # Not reachable. All possible types have been considered.
+    raise TypeError(f"Unexpected type: {inspect(type_)}.")
+
+
+_type_names_for_extension_kinds = {
+    "scalar_type_extension": "scalar",
+    "object_type_extension": "object",
+    "interface_type_extension": "interface",
+    "union_type_extension": "union",
+    "enum_type_extension": "enum",
+    "input_object_type_extension": "input object",
+}
+
+
+def extension_kind_to_type_name(kind: str) -> str:
+    return _type_names_for_extension_kinds.get(kind, "unknown type")
diff --git a/src/graphql/validation/rules/provided_required_arguments.py b/src/graphql/validation/rules/provided_required_arguments.py
new file mode 100644
index 0000000..1e2ba93
--- /dev/null
+++ b/src/graphql/validation/rules/provided_required_arguments.py
@@ -0,0 +1,120 @@
+from typing import cast, Any, Dict, List, Union
+
+from ...error import GraphQLError
+from ...language import (
+    DirectiveDefinitionNode,
+    DirectiveNode,
+    FieldNode,
+    InputValueDefinitionNode,
+    NonNullTypeNode,
+    TypeNode,
+    VisitorAction,
+    SKIP,
+    print_ast,
+)
+from ...pyutils import FrozenList
+from ...type import GraphQLArgument, is_required_argument, is_type, specified_directives
+from . import ASTValidationRule, SDLValidationContext, ValidationContext
+
+__all__ = ["ProvidedRequiredArgumentsRule", "ProvidedRequiredArgumentsOnDirectivesRule"]
+
+
+class ProvidedRequiredArgumentsOnDirectivesRule(ASTValidationRule):
+    """Provided required arguments on directives
+
+    A directive is only valid if all required (non-null without a default value)
+    arguments have been provided.
+
+    For internal use only.
+    """
+
+    context: Union[ValidationContext, SDLValidationContext]
+
+    def __init__(self, context: Union[ValidationContext, SDLValidationContext]):
+        super().__init__(context)
+        required_args_map: Dict[
+            str, Dict[str, Union[GraphQLArgument, InputValueDefinitionNode]]
+        ] = {}
+
+        schema = context.schema
+        defined_directives = schema.directives if schema else specified_directives
+        for directive in cast(List, defined_directives):
+            required_args_map[directive.name] = {
+                name: arg
+                for name, arg in directive.args.items()
+                if is_required_argument(arg)
+            }
+
+        ast_definitions = context.document.definitions
+        for def_ in ast_definitions:
+            if isinstance(def_, DirectiveDefinitionNode):
+                required_args_map[def_.name.value] = {
+                    arg.name.value: arg
+                    for arg in filter(is_required_argument_node, def_.arguments or [])
+                }
+
+        self.required_args_map = required_args_map
+
+    def leave_directive(self, directive_node: DirectiveNode, *_args: Any) -> None:
+        # Validate on leave to allow for deeper errors to appear first.
+        directive_name = directive_node.name.value
+        required_args = self.required_args_map.get(directive_name)
+        if required_args:
+
+            arg_nodes = directive_node.arguments or FrozenList()
+            arg_node_set = {arg.name.value for arg in arg_nodes}
+            for arg_name in required_args:
+                if arg_name not in arg_node_set:
+                    arg_type = required_args[arg_name].type
+                    arg_type_str = (
+                        str(arg_type)
+                        if is_type(arg_type)
+                        else print_ast(cast(TypeNode, arg_type))
+                    )
+                    self.report_error(
+                        GraphQLError(
+                            f"Directive '@{directive_name}' argument '{arg_name}'"
+                            f" of type '{arg_type_str}' is required,"
+                            " but it was not provided.",
+                            directive_node,
+                        )
+                    )
+
+
+class ProvidedRequiredArgumentsRule(ProvidedRequiredArgumentsOnDirectivesRule):
+    """Provided required arguments
+
+    A field or directive is only valid if all required (non-null without a default
+    value) field arguments have been provided.
+    """
+
+    context: ValidationContext
+
+    def __init__(self, context: ValidationContext):
+        super().__init__(context)
+
+    def leave_field(self, field_node: FieldNode, *_args: Any) -> VisitorAction:
+        # Validate on leave to allow for deeper errors to appear first.
+        field_def = self.context.get_field_def()
+        if not field_def:
+            return SKIP
+        arg_nodes = field_node.arguments or FrozenList()
+
+        arg_node_map = {arg.name.value: arg for arg in arg_nodes}
+        for arg_name, arg_def in field_def.args.items():
+            arg_node = arg_node_map.get(arg_name)
+            if not arg_node and is_required_argument(arg_def):
+                self.report_error(
+                    GraphQLError(
+                        f"Field '{field_node.name.value}' argument '{arg_name}'"
+                        f" of type '{arg_def.type}' is required,"
+                        " but it was not provided.",
+                        field_node,
+                    )
+                )
+
+        return None
+
+
+def is_required_argument_node(arg: InputValueDefinitionNode) -> bool:
+    return isinstance(arg.type, NonNullTypeNode) and arg.default_value is None
diff --git a/src/graphql/validation/rules/scalar_leafs.py b/src/graphql/validation/rules/scalar_leafs.py
new file mode 100644
index 0000000..174df4e
--- /dev/null
+++ b/src/graphql/validation/rules/scalar_leafs.py
@@ -0,0 +1,41 @@
+from typing import Any
+
+from ...error import GraphQLError
+from ...language import FieldNode
+from ...type import get_named_type, is_leaf_type
+from . import ValidationRule
+
+__all__ = ["ScalarLeafsRule"]
+
+
+class ScalarLeafsRule(ValidationRule):
+    """Scalar leafs
+
+    A GraphQL document is valid only if all leaf fields (fields without sub selections)
+    are of scalar or enum types.
+    """
+
+    def enter_field(self, node: FieldNode, *_args: Any) -> None:
+        type_ = self.context.get_type()
+        if type_:
+            selection_set = node.selection_set
+            if is_leaf_type(get_named_type(type_)):
+                if selection_set:
+                    field_name = node.name.value
+                    self.report_error(
+                        GraphQLError(
+                            f"Field '{field_name}' must not have a selection"
+                            f" since type '{type_}' has no subfields.",
+                            selection_set,
+                        )
+                    )
+            elif not selection_set:
+                field_name = node.name.value
+                self.report_error(
+                    GraphQLError(
+                        f"Field '{field_name}' of type '{type_}'"
+                        " must have a selection of subfields."
+                        f" Did you mean '{field_name} {{ ... }}'?",
+                        node,
+                    )
+                )
diff --git a/src/graphql/validation/rules/single_field_subscriptions.py b/src/graphql/validation/rules/single_field_subscriptions.py
new file mode 100644
index 0000000..d16b9cb
--- /dev/null
+++ b/src/graphql/validation/rules/single_field_subscriptions.py
@@ -0,0 +1,31 @@
+from typing import Any
+
+from ...error import GraphQLError
+from ...language import OperationDefinitionNode, OperationType
+from . import ASTValidationRule
+
+__all__ = ["SingleFieldSubscriptionsRule"]
+
+
+class SingleFieldSubscriptionsRule(ASTValidationRule):
+    """Subscriptions must only include one field.
+
+    A GraphQL subscription is valid only if it contains a single root.
+    """
+
+    def enter_operation_definition(
+        self, node: OperationDefinitionNode, *_args: Any
+    ) -> None:
+        if node.operation == OperationType.SUBSCRIPTION:
+            if len(node.selection_set.selections) != 1:
+                self.report_error(
+                    GraphQLError(
+                        (
+                            f"Subscription '{node.name.value}'"
+                            if node.name
+                            else "Anonymous Subscription"
+                        )
+                        + " must select only one top level field.",
+                        node.selection_set.selections[1:],
+                    )
+                )
diff --git a/src/graphql/validation/rules/unique_argument_names.py b/src/graphql/validation/rules/unique_argument_names.py
new file mode 100644
index 0000000..40fc812
--- /dev/null
+++ b/src/graphql/validation/rules/unique_argument_names.py
@@ -0,0 +1,39 @@
+from typing import Any, Dict
+
+from ...error import GraphQLError
+from ...language import ArgumentNode, NameNode, VisitorAction, SKIP
+from . import ASTValidationContext, ASTValidationRule
+
+__all__ = ["UniqueArgumentNamesRule"]
+
+
+class UniqueArgumentNamesRule(ASTValidationRule):
+    """Unique argument names
+
+    A GraphQL field or directive is only valid if all supplied arguments are uniquely
+    named.
+    """
+
+    def __init__(self, context: ASTValidationContext):
+        super().__init__(context)
+        self.known_arg_names: Dict[str, NameNode] = {}
+
+    def enter_field(self, *_args: Any) -> None:
+        self.known_arg_names.clear()
+
+    def enter_directive(self, *_args: Any) -> None:
+        self.known_arg_names.clear()
+
+    def enter_argument(self, node: ArgumentNode, *_args: Any) -> VisitorAction:
+        known_arg_names = self.known_arg_names
+        arg_name = node.name.value
+        if arg_name in known_arg_names:
+            self.report_error(
+                GraphQLError(
+                    f"There can be only one argument named '{arg_name}'.",
+                    [known_arg_names[arg_name], node.name],
+                )
+            )
+        else:
+            known_arg_names[arg_name] = node.name
+        return SKIP
diff --git a/src/graphql/validation/rules/unique_directive_names.py b/src/graphql/validation/rules/unique_directive_names.py
new file mode 100644
index 0000000..b25dafe
--- /dev/null
+++ b/src/graphql/validation/rules/unique_directive_names.py
@@ -0,0 +1,46 @@
+from typing import Any, Dict
+
+from ...error import GraphQLError
+from ...language import DirectiveDefinitionNode, NameNode, VisitorAction, SKIP
+from . import SDLValidationContext, SDLValidationRule
+
+__all__ = ["UniqueDirectiveNamesRule"]
+
+
+class UniqueDirectiveNamesRule(SDLValidationRule):
+    """Unique directive names
+
+    A GraphQL document is only valid if all defined directives have unique names.
+    """
+
+    def __init__(self, context: SDLValidationContext):
+        super().__init__(context)
+        self.known_directive_names: Dict[str, NameNode] = {}
+        self.schema = context.schema
+
+    def enter_directive_definition(
+        self, node: DirectiveDefinitionNode, *_args: Any
+    ) -> VisitorAction:
+        directive_name = node.name.value
+
+        if self.schema and self.schema.get_directive(directive_name):
+            self.report_error(
+                GraphQLError(
+                    f"Directive '@{directive_name}' already exists in the schema."
+                    " It cannot be redefined.",
+                    node.name,
+                )
+            )
+        else:
+            if directive_name in self.known_directive_names:
+                self.report_error(
+                    GraphQLError(
+                        f"There can be only one directive named '@{directive_name}'.",
+                        [self.known_directive_names[directive_name], node.name],
+                    )
+                )
+            else:
+                self.known_directive_names[directive_name] = node.name
+            return SKIP
+
+        return None
diff --git a/src/graphql/validation/rules/unique_directives_per_location.py b/src/graphql/validation/rules/unique_directives_per_location.py
new file mode 100644
index 0000000..37fcd87
--- /dev/null
+++ b/src/graphql/validation/rules/unique_directives_per_location.py
@@ -0,0 +1,82 @@
+from collections import defaultdict
+from typing import Any, Dict, List, Union, cast
+
+from ...error import GraphQLError
+from ...language import (
+    DirectiveDefinitionNode,
+    DirectiveNode,
+    Node,
+    SchemaDefinitionNode,
+    SchemaExtensionNode,
+    TypeDefinitionNode,
+    TypeExtensionNode,
+    is_type_definition_node,
+    is_type_extension_node,
+)
+from ...type import specified_directives
+from . import ASTValidationRule, SDLValidationContext, ValidationContext
+
+__all__ = ["UniqueDirectivesPerLocationRule"]
+
+
+class UniqueDirectivesPerLocationRule(ASTValidationRule):
+    """Unique directive names per location
+
+    A GraphQL document is only valid if all non-repeatable directives at a given
+    location are uniquely named.
+    """
+
+    context: Union[ValidationContext, SDLValidationContext]
+
+    def __init__(self, context: Union[ValidationContext, SDLValidationContext]):
+        super().__init__(context)
+        unique_directive_map: Dict[str, bool] = {}
+
+        schema = context.schema
+        defined_directives = (
+            schema.directives if schema else cast(List, specified_directives)
+        )
+        for directive in defined_directives:
+            unique_directive_map[directive.name] = not directive.is_repeatable
+
+        ast_definitions = context.document.definitions
+        for def_ in ast_definitions:
+            if isinstance(def_, DirectiveDefinitionNode):
+                unique_directive_map[def_.name.value] = not def_.repeatable
+        self.unique_directive_map = unique_directive_map
+
+        self.schema_directives: Dict[str, DirectiveNode] = {}
+        self.type_directives_map: Dict[str, Dict[str, DirectiveNode]] = defaultdict(
+            dict
+        )
+
+    # Many different AST nodes may contain directives. Rather than listing them all,
+    # just listen for entering any node, and check to see if it defines any directives.
+    def enter(self, node: Node, *_args: Any) -> None:
+        directives: List[DirectiveNode] = getattr(node, "directives", None)
+        if not directives:
+            return
+
+        if isinstance(node, (SchemaDefinitionNode, SchemaExtensionNode)):
+            seen_directives = self.schema_directives
+        elif is_type_definition_node(node) or is_type_extension_node(node):
+            node = cast(Union[TypeDefinitionNode, TypeExtensionNode], node)
+            type_name = node.name.value
+            seen_directives = self.type_directives_map[type_name]
+        else:
+            seen_directives = {}
+
+        for directive in directives:
+            directive_name = directive.name.value
+
+            if self.unique_directive_map.get(directive_name):
+                if directive_name in seen_directives:
+                    self.report_error(
+                        GraphQLError(
+                            f"The directive '@{directive_name}'"
+                            " can only be used once at this location.",
+                            [seen_directives[directive_name], directive],
+                        )
+                    )
+                else:
+                    seen_directives[directive_name] = directive
diff --git a/src/graphql/validation/rules/unique_enum_value_names.py b/src/graphql/validation/rules/unique_enum_value_names.py
new file mode 100644
index 0000000..9be41e8
--- /dev/null
+++ b/src/graphql/validation/rules/unique_enum_value_names.py
@@ -0,0 +1,61 @@
+from collections import defaultdict
+from typing import cast, Any, Dict
+
+from ...error import GraphQLError
+from ...language import NameNode, EnumTypeDefinitionNode, VisitorAction, SKIP
+from ...type import is_enum_type, GraphQLEnumType
+from . import SDLValidationContext, SDLValidationRule
+
+__all__ = ["UniqueEnumValueNamesRule"]
+
+
+class UniqueEnumValueNamesRule(SDLValidationRule):
+    """Unique enum value names
+
+    A GraphQL enum type is only valid if all its values are uniquely named.
+    """
+
+    def __init__(self, context: SDLValidationContext):
+        super().__init__(context)
+        schema = context.schema
+        self.existing_type_map = schema.type_map if schema else {}
+        self.known_value_names: Dict[str, Dict[str, NameNode]] = defaultdict(dict)
+
+    def check_value_uniqueness(
+        self, node: EnumTypeDefinitionNode, *_args: Any
+    ) -> VisitorAction:
+        existing_type_map = self.existing_type_map
+        type_name = node.name.value
+        value_names = self.known_value_names[type_name]
+
+        for value_def in node.values or []:
+            value_name = value_def.name.value
+
+            existing_type = existing_type_map.get(type_name)
+            if (
+                is_enum_type(existing_type)
+                and value_name in cast(GraphQLEnumType, existing_type).values
+            ):
+                self.report_error(
+                    GraphQLError(
+                        f"Enum value '{type_name}.{value_name}'"
+                        " already exists in the schema."
+                        " It cannot also be defined in this type extension.",
+                        value_def.name,
+                    )
+                )
+            elif value_name in value_names:
+                self.report_error(
+                    GraphQLError(
+                        f"Enum value '{type_name}.{value_name}'"
+                        " can only be defined once.",
+                        [value_names[value_name], value_def.name],
+                    )
+                )
+            else:
+                value_names[value_name] = value_def.name
+
+        return SKIP
+
+    enter_enum_type_definition = check_value_uniqueness
+    enter_enum_type_extension = check_value_uniqueness
diff --git a/src/graphql/validation/rules/unique_field_definition_names.py b/src/graphql/validation/rules/unique_field_definition_names.py
new file mode 100644
index 0000000..fe29d6d
--- /dev/null
+++ b/src/graphql/validation/rules/unique_field_definition_names.py
@@ -0,0 +1,67 @@
+from collections import defaultdict
+from typing import Any, Dict
+
+from ...error import GraphQLError
+from ...language import NameNode, ObjectTypeDefinitionNode, VisitorAction, SKIP
+from ...type import is_object_type, is_interface_type, is_input_object_type
+from . import SDLValidationContext, SDLValidationRule
+
+__all__ = ["UniqueFieldDefinitionNamesRule"]
+
+
+class UniqueFieldDefinitionNamesRule(SDLValidationRule):
+    """Unique field definition names
+
+    A GraphQL complex type is only valid if all its fields are uniquely named.
+    """
+
+    def __init__(self, context: SDLValidationContext):
+        super().__init__(context)
+        schema = context.schema
+        self.existing_type_map = schema.type_map if schema else {}
+        self.known_field_names: Dict[str, Dict[str, NameNode]] = defaultdict(dict)
+
+    def check_field_uniqueness(
+        self, node: ObjectTypeDefinitionNode, *_args: Any
+    ) -> VisitorAction:
+        existing_type_map = self.existing_type_map
+        type_name = node.name.value
+        field_names = self.known_field_names[type_name]
+
+        for field_def in node.fields or []:
+            field_name = field_def.name.value
+
+            if has_field(existing_type_map.get(type_name), field_name):
+                self.report_error(
+                    GraphQLError(
+                        f"Field '{type_name}.{field_name}'"
+                        " already exists in the schema."
+                        " It cannot also be defined in this type extension.",
+                        field_def.name,
+                    )
+                )
+            elif field_name in field_names:
+                self.report_error(
+                    GraphQLError(
+                        f"Field '{type_name}.{field_name}'"
+                        " can only be defined once.",
+                        [field_names[field_name], field_def.name],
+                    )
+                )
+            else:
+                field_names[field_name] = field_def.name
+
+        return SKIP
+
+    enter_input_object_type_definition = check_field_uniqueness
+    enter_input_object_type_extension = check_field_uniqueness
+    enter_interface_type_definition = check_field_uniqueness
+    enter_interface_type_extension = check_field_uniqueness
+    enter_object_type_definition = check_field_uniqueness
+    enter_object_type_extension = check_field_uniqueness
+
+
+def has_field(type_: Any, field_name: str) -> bool:
+    if is_object_type(type_) or is_interface_type(type_) or is_input_object_type(type_):
+        return field_name in type_.fields
+    return False
diff --git a/src/graphql/validation/rules/unique_fragment_names.py b/src/graphql/validation/rules/unique_fragment_names.py
new file mode 100644
index 0000000..438dded
--- /dev/null
+++ b/src/graphql/validation/rules/unique_fragment_names.py
@@ -0,0 +1,38 @@
+from typing import Any, Dict
+
+from ...error import GraphQLError
+from ...language import NameNode, FragmentDefinitionNode, VisitorAction, SKIP
+from . import ASTValidationContext, ASTValidationRule
+
+__all__ = ["UniqueFragmentNamesRule"]
+
+
+class UniqueFragmentNamesRule(ASTValidationRule):
+    """Unique fragment names
+
+    A GraphQL document is only valid if all defined fragments have unique names.
+    """
+
+    def __init__(self, context: ASTValidationContext):
+        super().__init__(context)
+        self.known_fragment_names: Dict[str, NameNode] = {}
+
+    @staticmethod
+    def enter_operation_definition(*_args: Any) -> VisitorAction:
+        return SKIP
+
+    def enter_fragment_definition(
+        self, node: FragmentDefinitionNode, *_args: Any
+    ) -> VisitorAction:
+        known_fragment_names = self.known_fragment_names
+        fragment_name = node.name.value
+        if fragment_name in known_fragment_names:
+            self.report_error(
+                GraphQLError(
+                    f"There can be only one fragment named '{fragment_name}'.",
+                    [known_fragment_names[fragment_name], node.name],
+                )
+            )
+        else:
+            known_fragment_names[fragment_name] = node.name
+        return SKIP
diff --git a/src/graphql/validation/rules/unique_input_field_names.py b/src/graphql/validation/rules/unique_input_field_names.py
new file mode 100644
index 0000000..824d648
--- /dev/null
+++ b/src/graphql/validation/rules/unique_input_field_names.py
@@ -0,0 +1,40 @@
+from typing import Any, Dict, List
+
+from ...error import GraphQLError
+from ...language import NameNode, ObjectFieldNode
+from . import ASTValidationContext, ASTValidationRule
+
+__all__ = ["UniqueInputFieldNamesRule"]
+
+
+class UniqueInputFieldNamesRule(ASTValidationRule):
+    """Unique input field names
+
+    A GraphQL input object value is only valid if all supplied fields are uniquely
+    named.
+    """
+
+    def __init__(self, context: ASTValidationContext):
+        super().__init__(context)
+        self.known_names_stack: List[Dict[str, NameNode]] = []
+        self.known_names: Dict[str, NameNode] = {}
+
+    def enter_object_value(self, *_args: Any) -> None:
+        self.known_names_stack.append(self.known_names)
+        self.known_names = {}
+
+    def leave_object_value(self, *_args: Any) -> None:
+        self.known_names = self.known_names_stack.pop()
+
+    def enter_object_field(self, node: ObjectFieldNode, *_args: Any) -> None:
+        known_names = self.known_names
+        field_name = node.name.value
+        if field_name in known_names:
+            self.report_error(
+                GraphQLError(
+                    f"There can be only one input field named '{field_name}'.",
+                    [known_names[field_name], node.name],
+                )
+            )
+        else:
+            known_names[field_name] = node.name
diff --git a/src/graphql/validation/rules/unique_operation_names.py b/src/graphql/validation/rules/unique_operation_names.py
new file mode 100644
index 0000000..f5f3b21
--- /dev/null
+++ b/src/graphql/validation/rules/unique_operation_names.py
@@ -0,0 +1,40 @@
+from typing import Any, Dict
+
+from ...error import GraphQLError
+from ...language import NameNode, OperationDefinitionNode, VisitorAction, SKIP
+from . import ASTValidationContext, ASTValidationRule
+
+__all__ = ["UniqueOperationNamesRule"]
+
+
+class UniqueOperationNamesRule(ASTValidationRule):
+    """Unique operation names
+
+    A GraphQL document is only valid if all defined operations have unique names.
+    """
+
+    def __init__(self, context: ASTValidationContext):
+        super().__init__(context)
+        self.known_operation_names: Dict[str, NameNode] = {}
+
+    def enter_operation_definition(
+        self, node: OperationDefinitionNode, *_args: Any
+    ) -> VisitorAction:
+        operation_name = node.name
+        if operation_name:
+            known_operation_names = self.known_operation_names
+            if operation_name.value in known_operation_names:
+                self.report_error(
+                    GraphQLError(
+                        "There can be only one operation"
+                        f" named '{operation_name.value}'.",
+                        [known_operation_names[operation_name.value], operation_name],
+                    )
+                )
+            else:
+                known_operation_names[operation_name.value] = operation_name
+        return SKIP
+
+    @staticmethod
+    def enter_fragment_definition(*_args: Any) -> VisitorAction:
+        return SKIP
diff --git a/src/graphql/validation/rules/unique_operation_types.py b/src/graphql/validation/rules/unique_operation_types.py
new file mode 100644
index 0000000..190963f
--- /dev/null
+++ b/src/graphql/validation/rules/unique_operation_types.py
@@ -0,0 +1,69 @@
+from typing import Any, Dict, Optional, Union
+
+from ...error import GraphQLError
+from ...language import (
+    OperationTypeDefinitionNode,
+    OperationType,
+    SchemaDefinitionNode,
+    SchemaExtensionNode,
+    VisitorAction,
+    SKIP,
+)
+from ...type import GraphQLObjectType
+from . import SDLValidationContext, SDLValidationRule
+
+__all__ = ["UniqueOperationTypesRule"]
+
+
+class UniqueOperationTypesRule(SDLValidationRule):
+    """Unique operation types
+
+    A GraphQL document is only valid if it has only one type per operation.
+    """
+
+    def __init__(self, context: SDLValidationContext):
+        super().__init__(context)
+        schema = context.schema
+        self.defined_operation_types: Dict[
+            OperationType, OperationTypeDefinitionNode
+        ] = {}
+        self.existing_operation_types: Dict[
+            OperationType, Optional[GraphQLObjectType]
+        ] = (
+            {
+                OperationType.QUERY: schema.query_type,
+                OperationType.MUTATION: schema.mutation_type,
+                OperationType.SUBSCRIPTION: schema.subscription_type,
+            }
+            if schema
+            else {}
+        )
+        self.schema = schema
+
+    def check_operation_types(
+        self, node: Union[SchemaDefinitionNode, SchemaExtensionNode], *_args: Any
+    ) -> VisitorAction:
+        for operation_type in node.operation_types or []:
+            operation = operation_type.operation
+            already_defined_operation_type = self.defined_operation_types.get(operation)
+
+            if self.existing_operation_types.get(operation):
+                self.report_error(
+                    GraphQLError(
+                        f"Type for {operation.value} already defined in the schema."
+                        " It cannot be redefined.",
+                        operation_type,
+                    )
+                )
+            elif already_defined_operation_type:
+                self.report_error(
+                    GraphQLError(
+                        f"There can be only one {operation.value} type in schema.",
+                        [already_defined_operation_type, operation_type],
+                    )
+                )
+            else:
+                self.defined_operation_types[operation] = operation_type
+        return SKIP
+
+    enter_schema_definition = enter_schema_extension = check_operation_types
diff --git a/src/graphql/validation/rules/unique_type_names.py b/src/graphql/validation/rules/unique_type_names.py
new file mode 100644
index 0000000..7082a0f
--- /dev/null
+++ b/src/graphql/validation/rules/unique_type_names.py
@@ -0,0 +1,48 @@
+from typing import Any, Dict
+
+from ...error import GraphQLError
+from ...language import NameNode, TypeDefinitionNode, VisitorAction, SKIP
+from . import SDLValidationContext, SDLValidationRule
+
+__all__ = ["UniqueTypeNamesRule"]
+
+
+class UniqueTypeNamesRule(SDLValidationRule):
+    """Unique type names
+
+    A GraphQL document is only valid if all defined types have unique names.
+    """
+
+    def __init__(self, context: SDLValidationContext):
+        super().__init__(context)
+        self.known_type_names: Dict[str, NameNode] = {}
+        self.schema = context.schema
+
+    def check_type_name(self, node: TypeDefinitionNode, *_args: Any) -> VisitorAction:
+        type_name = node.name.value
+
+        if self.schema and self.schema.get_type(type_name):
+            self.report_error(
+                GraphQLError(
+                    f"Type '{type_name}' already exists in the schema."
+                    " It cannot also be defined in this type definition.",
+                    node.name,
+                )
+            )
+        else:
+            if type_name in self.known_type_names:
+                self.report_error(
+                    GraphQLError(
+                        f"There can be only one type named '{type_name}'.",
+                        [self.known_type_names[type_name], node.name],
+                    )
+                )
+            else:
+                self.known_type_names[type_name] = node.name
+            return SKIP
+
+        return None
+
+    enter_scalar_type_definition = enter_object_type_definition = check_type_name
+    enter_interface_type_definition = enter_union_type_definition = check_type_name
+    enter_enum_type_definition = enter_input_object_type_definition = check_type_name
diff --git a/src/graphql/validation/rules/unique_variable_names.py b/src/graphql/validation/rules/unique_variable_names.py
new file mode 100644
index 0000000..1c0e963
--- /dev/null
+++ b/src/graphql/validation/rules/unique_variable_names.py
@@ -0,0 +1,36 @@
+from typing import Any, Dict
+
+from ...error import GraphQLError
+from ...language import NameNode, VariableDefinitionNode
+from . import ASTValidationContext, ASTValidationRule
+
+__all__ = ["UniqueVariableNamesRule"]
+
+
+class UniqueVariableNamesRule(ASTValidationRule):
+    """Unique variable names
+
+    A GraphQL operation is only valid if all its variables are uniquely named.
+    """
+
+    def __init__(self, context: ASTValidationContext):
+        super().__init__(context)
+        self.known_variable_names: Dict[str, NameNode] = {}
+
+    def enter_operation_definition(self, *_args: Any) -> None:
+        self.known_variable_names.clear()
+
+    def enter_variable_definition(
+        self, node: VariableDefinitionNode, *_args: Any
+    ) -> None:
+        known_variable_names = self.known_variable_names
+        variable_name = node.variable.name.value
+        if variable_name in known_variable_names:
+            self.report_error(
+                GraphQLError(
+                    f"There can be only one variable named '${variable_name}'.",
+                    [known_variable_names[variable_name], node.variable.name],
+                )
+            )
+        else:
+            known_variable_names[variable_name] = node.variable.name
diff --git a/src/graphql/validation/rules/values_of_correct_type.py b/src/graphql/validation/rules/values_of_correct_type.py
new file mode 100644
index 0000000..3ad839b
--- /dev/null
+++ b/src/graphql/validation/rules/values_of_correct_type.py
@@ -0,0 +1,161 @@
+from typing import cast, Any
+
+from ...error import GraphQLError
+from ...language import (
+    BooleanValueNode,
+    EnumValueNode,
+    FloatValueNode,
+    IntValueNode,
+    NullValueNode,
+    ListValueNode,
+    ObjectFieldNode,
+    ObjectValueNode,
+    StringValueNode,
+    ValueNode,
+    VisitorAction,
+    SKIP,
+    print_ast,
+)
+from ...pyutils import did_you_mean, suggestion_list, Undefined
+from ...type import (
+    GraphQLInputObjectType,
+    GraphQLScalarType,
+    get_named_type,
+    get_nullable_type,
+    is_input_object_type,
+    is_leaf_type,
+    is_list_type,
+    is_non_null_type,
+    is_required_input_field,
+)
+from . import ValidationRule
+
+__all__ = ["ValuesOfCorrectTypeRule"]
+
+
+class ValuesOfCorrectTypeRule(ValidationRule):
+    """Value literals of correct type
+
+    A GraphQL document is only valid if all value literals are of the type expected at
+    their position.
+    """
+
+    def enter_list_value(self, node: ListValueNode, *_args: Any) -> VisitorAction:
+        # Note: TypeInfo will traverse into a list's item type, so look to the parent
+        # input type to check if it is a list.
+        type_ = get_nullable_type(self.context.get_parent_input_type())  # type: ignore
+        if not is_list_type(type_):
+            self.is_valid_value_node(node)
+            return SKIP  # Don't traverse further.
+        return None
+
+    def enter_object_value(self, node: ObjectValueNode, *_args: Any) -> VisitorAction:
+        type_ = get_named_type(self.context.get_input_type())
+        if not is_input_object_type(type_):
+            self.is_valid_value_node(node)
+            return SKIP  # Don't traverse further.
+        type_ = cast(GraphQLInputObjectType, type_)
+        # Ensure every required field exists.
+        field_node_map = {field.name.value: field for field in node.fields}
+        for field_name, field_def in type_.fields.items():
+            field_node = field_node_map.get(field_name)
+            if not field_node and is_required_input_field(field_def):
+                field_type = field_def.type
+                self.report_error(
+                    GraphQLError(
+                        f"Field '{type_.name}.{field_name}' of required type"
+                        f" '{field_type}' was not provided.",
+                        node,
+                    )
+                )
+        return None
+
+    def enter_object_field(self, node: ObjectFieldNode, *_args: Any) -> None:
+        parent_type = get_named_type(self.context.get_parent_input_type())
+        field_type = self.context.get_input_type()
+        if not field_type and is_input_object_type(parent_type):
+            parent_type = cast(GraphQLInputObjectType, parent_type)
+            suggestions = suggestion_list(node.name.value, list(parent_type.fields))
+            self.report_error(
+                GraphQLError(
+                    f"Field '{node.name.value}'"
+                    f" is not defined by type '{parent_type.name}'."
+                    + did_you_mean(suggestions),
+                    node,
+                )
+            )
+
+    def enter_null_value(self, node: NullValueNode, *_args: Any) -> None:
+        type_ = self.context.get_input_type()
+        if is_non_null_type(type_):
+            self.report_error(
+                GraphQLError(
+                    f"Expected value of type '{type_}', found {print_ast(node)}.", node
+                )
+            )
+
+    def enter_enum_value(self, node: EnumValueNode, *_args: Any) -> None:
+        self.is_valid_value_node(node)
+
+    def enter_int_value(self, node: IntValueNode, *_args: Any) -> None:
+        self.is_valid_value_node(node)
+
+    def enter_float_value(self, node: FloatValueNode, *_args: Any) -> None:
+        self.is_valid_value_node(node)
+
+    def enter_string_value(self, node: StringValueNode, *_args: Any) -> None:
+        self.is_valid_value_node(node)
+
+    def enter_boolean_value(self, node: BooleanValueNode, *_args: Any) -> None:
+        self.is_valid_value_node(node)
+
+    def is_valid_value_node(self, node: ValueNode) -> None:
+        """Check whether this is a valid value node.
+
+        Any value literal may be a valid representation of a Scalar, depending on that
+        scalar type.
+        """
+        # Report any error at the full type expected by the location.
+        location_type = self.context.get_input_type()
+        if not location_type:
+            return
+
+        type_ = get_named_type(location_type)
+
+        if not is_leaf_type(type_):
+            self.report_error(
+                GraphQLError(
+                    f"Expected value of type '{location_type}',"
+                    f" found {print_ast(node)}.",
+                    node,
+                )
+            )
+            return
+
+        # Scalars determine if a literal value is valid via `parse_literal()` which may
+        # throw or return an invalid value to indicate failure.
+        type_ = cast(GraphQLScalarType, type_)
+        try:
+            parse_result = type_.parse_literal(node)
+            if parse_result is Undefined:
+                self.report_error(
+                    GraphQLError(
+                        f"Expected value of type '{location_type}',"
+                        f" found {print_ast(node)}.",
+                        node,
+                    )
+                )
+        except GraphQLError as error:
+            self.report_error(error)
+        except Exception as error:
+            self.report_error(
+                GraphQLError(
+                    f"Expected value of type '{location_type}',"
+                    f" found {print_ast(node)}; {error}",
+                    node,
+                    # Ensure a reference to the original error is maintained.
+                    original_error=error,
+                )
+            )
+
+        return
diff --git a/src/graphql/validation/rules/variables_are_input_types.py b/src/graphql/validation/rules/variables_are_input_types.py
new file mode 100644
index 0000000..7664e61
--- /dev/null
+++ b/src/graphql/validation/rules/variables_are_input_types.py
@@ -0,0 +1,34 @@
+from typing import Any
+
+from ...error import GraphQLError
+from ...language import VariableDefinitionNode, print_ast
+from ...type import is_input_type
+from ...utilities import type_from_ast
+from . import ValidationRule
+
+__all__ = ["VariablesAreInputTypesRule"]
+
+
+class VariablesAreInputTypesRule(ValidationRule):
+    """Variables are input types
+
+    A GraphQL operation is only valid if all the variables it defines are of input types
+    (scalar, enum, or input object).
+    """
+
+    def enter_variable_definition(
+        self, node: VariableDefinitionNode, *_args: Any
+    ) -> None:
+        type_ = type_from_ast(self.context.schema, node.type)
+
+        # If the variable type is not an input type, return an error.
+        if type_ and not is_input_type(type_):
+            variable_name = node.variable.name.value
+            type_name = print_ast(node.type)
+            self.report_error(
+                GraphQLError(
+                    f"Variable '${variable_name}'"
+                    f" cannot be non-input type '{type_name}'.",
+                    node.type,
+                )
+            )
diff --git a/src/graphql/validation/rules/variables_in_allowed_position.py b/src/graphql/validation/rules/variables_in_allowed_position.py
new file mode 100644
index 0000000..bade043
--- /dev/null
+++ b/src/graphql/validation/rules/variables_in_allowed_position.py
@@ -0,0 +1,88 @@
+from typing import Any, Dict, Optional, cast
+
+from ...error import GraphQLError
+from ...language import (
+    NullValueNode,
+    OperationDefinitionNode,
+    ValueNode,
+    VariableDefinitionNode,
+)
+from ...pyutils import Undefined
+from ...type import GraphQLNonNull, GraphQLSchema, GraphQLType, is_non_null_type
+from ...utilities import type_from_ast, is_type_sub_type_of
+from . import ValidationContext, ValidationRule
+
+__all__ = ["VariablesInAllowedPositionRule"]
+
+
+class VariablesInAllowedPositionRule(ValidationRule):
+    """Variables passed to field arguments conform to type"""
+
+    def __init__(self, context: ValidationContext):
+        super().__init__(context)
+        self.var_def_map: Dict[str, Any] = {}
+
+    def enter_operation_definition(self, *_args: Any) -> None:
+        self.var_def_map.clear()
+
+    def leave_operation_definition(
+        self, operation: OperationDefinitionNode, *_args: Any
+    ) -> None:
+        var_def_map = self.var_def_map
+        usages = self.context.get_recursive_variable_usages(operation)
+
+        for usage in usages:
+            node, type_ = usage.node, usage.type
+            default_value = usage.default_value
+            var_name = node.name.value
+            var_def = var_def_map.get(var_name)
+            if var_def and type_:
+                # A var type is allowed if it is the same or more strict (e.g. is a
+                # subtype of) than the expected type. It can be more strict if the
+                # variable type is non-null when the expected type is nullable. If both
+                # are list types, the variable item type can be more strict than the
+                # expected item type (contravariant).
+                schema = self.context.schema
+                var_type = type_from_ast(schema, var_def.type)
+                if var_type and not allowed_variable_usage(
+                    schema, var_type, var_def.default_value, type_, default_value
+                ):
+                    self.report_error(
+                        GraphQLError(
+                            f"Variable '${var_name}' of type '{var_type}' used"
+                            f" in position expecting type '{type_}'.",
+                            [var_def, node],
+                        )
+                    )
+
+    def enter_variable_definition(
+        self, node: VariableDefinitionNode, *_args: Any
+    ) -> None:
+        self.var_def_map[node.variable.name.value] = node
+
+
+def allowed_variable_usage(
+    schema: GraphQLSchema,
+    var_type: GraphQLType,
+    var_default_value: Optional[ValueNode],
+    location_type: GraphQLType,
+    location_default_value: Any,
+) -> bool:
+    """Check for allowed variable usage.
+
+    Returns True if the variable is allowed in the location it was found, which includes
+    considering if default values exist for either the variable or the location at which
+    it is located.
+    """
+    if is_non_null_type(location_type) and not is_non_null_type(var_type):
+        has_non_null_variable_default_value = (
+            var_default_value is not None
+            and not isinstance(var_default_value, NullValueNode)
+        )
+        has_location_default_value = location_default_value is not Undefined
+        if not has_non_null_variable_default_value and not has_location_default_value:
+            return False
+        location_type = cast(GraphQLNonNull, location_type)
+        nullable_location_type = location_type.of_type
+        return is_type_sub_type_of(schema, var_type, nullable_location_type)
+    return is_type_sub_type_of(schema, var_type, location_type)
diff --git a/src/graphql/validation/specified_rules.py b/src/graphql/validation/specified_rules.py
new file mode 100644
index 0000000..dd0c263
--- /dev/null
+++ b/src/graphql/validation/specified_rules.py
@@ -0,0 +1,163 @@
+from typing import Type
+
+from ..pyutils import FrozenList
+
+from .rules import ASTValidationRule
+
+# Spec Section: "Executable Definitions"
+from .rules.executable_definitions import ExecutableDefinitionsRule
+
+# Spec Section: "Operation Name Uniqueness"
+from .rules.unique_operation_names import UniqueOperationNamesRule
+
+# Spec Section: "Lone Anonymous Operation"
+from .rules.lone_anonymous_operation import LoneAnonymousOperationRule
+
+# Spec Section: "Subscriptions with Single Root Field"
+from .rules.single_field_subscriptions import SingleFieldSubscriptionsRule
+
+# Spec Section: "Fragment Spread Type Existence"
+from .rules.known_type_names import KnownTypeNamesRule
+
+# Spec Section: "Fragments on Composite Types"
+from .rules.fragments_on_composite_types import FragmentsOnCompositeTypesRule
+
+# Spec Section: "Variables are Input Types"
+from .rules.variables_are_input_types import VariablesAreInputTypesRule
+
+# Spec Section: "Leaf Field Selections"
+from .rules.scalar_leafs import ScalarLeafsRule
+
+# Spec Section: "Field Selections on Objects, Interfaces, and Unions Types"
+from .rules.fields_on_correct_type import FieldsOnCorrectTypeRule
+
+# Spec Section: "Fragment Name Uniqueness"
+from .rules.unique_fragment_names import UniqueFragmentNamesRule
+
+# Spec Section: "Fragment spread target defined"
+from .rules.known_fragment_names import KnownFragmentNamesRule
+
+# Spec Section: "Fragments must be used"
+from .rules.no_unused_fragments import NoUnusedFragmentsRule
+
+# Spec Section: "Fragment spread is possible"
+from .rules.possible_fragment_spreads import PossibleFragmentSpreadsRule
+
+# Spec Section: "Fragments must not form cycles"
+from .rules.no_fragment_cycles import NoFragmentCyclesRule
+
+# Spec Section: "Variable Uniqueness"
+from .rules.unique_variable_names import UniqueVariableNamesRule
+
+# Spec Section: "All Variable Used Defined"
+from .rules.no_undefined_variables import NoUndefinedVariablesRule
+
+# Spec Section: "All Variables Used"
+from .rules.no_unused_variables import NoUnusedVariablesRule
+
+# Spec Section: "Directives Are Defined"
+from .rules.known_directives import KnownDirectivesRule
+
+# Spec Section: "Directives Are Unique Per Location"
+from .rules.unique_directives_per_location import UniqueDirectivesPerLocationRule
+
+# Spec Section: "Argument Names"
+from .rules.known_argument_names import KnownArgumentNamesRule
+from .rules.known_argument_names import KnownArgumentNamesOnDirectivesRule
+
+# Spec Section: "Argument Uniqueness"
+from .rules.unique_argument_names import UniqueArgumentNamesRule
+
+# Spec Section: "Value Type Correctness"
+from .rules.values_of_correct_type import ValuesOfCorrectTypeRule
+
+# Spec Section: "Argument Optionality"
+from .rules.provided_required_arguments import ProvidedRequiredArgumentsRule
+from .rules.provided_required_arguments import ProvidedRequiredArgumentsOnDirectivesRule
+
+# Spec Section: "All Variable Usages Are Allowed"
+from .rules.variables_in_allowed_position import VariablesInAllowedPositionRule
+
+# Spec Section: "Field Selection Merging"
+from .rules.overlapping_fields_can_be_merged import OverlappingFieldsCanBeMergedRule
+
+# Spec Section: "Input Object Field Uniqueness"
+from .rules.unique_input_field_names import UniqueInputFieldNamesRule
+
+# Schema definition language:
+from .rules.lone_schema_definition import LoneSchemaDefinitionRule
+from .rules.unique_operation_types import UniqueOperationTypesRule
+from .rules.unique_type_names import UniqueTypeNamesRule
+from .rules.unique_enum_value_names import UniqueEnumValueNamesRule
+from .rules.unique_field_definition_names import UniqueFieldDefinitionNamesRule
+from .rules.unique_directive_names import UniqueDirectiveNamesRule
+from .rules.possible_type_extensions import PossibleTypeExtensionsRule
+
+__all__ = ["specified_rules", "specified_sdl_rules"]
+
+
+# This list includes all validation rules defined by the GraphQL spec.
+#
+# The order of the rules in this list has been adjusted to lead to the
+# most clear output when encountering multiple validation errors.
+
+specified_rules: FrozenList[Type[ASTValidationRule]] = FrozenList(
+    [
+        ExecutableDefinitionsRule,
+        UniqueOperationNamesRule,
+        LoneAnonymousOperationRule,
+        SingleFieldSubscriptionsRule,
+        KnownTypeNamesRule,
+        FragmentsOnCompositeTypesRule,
+        VariablesAreInputTypesRule,
+        ScalarLeafsRule,
+        FieldsOnCorrectTypeRule,
+        UniqueFragmentNamesRule,
+        KnownFragmentNamesRule,
+        NoUnusedFragmentsRule,
+        PossibleFragmentSpreadsRule,
+        NoFragmentCyclesRule,
+        UniqueVariableNamesRule,
+        NoUndefinedVariablesRule,
+        NoUnusedVariablesRule,
+        KnownDirectivesRule,
+        UniqueDirectivesPerLocationRule,
+        KnownArgumentNamesRule,
+        UniqueArgumentNamesRule,
+        ValuesOfCorrectTypeRule,
+        ProvidedRequiredArgumentsRule,
+        VariablesInAllowedPositionRule,
+        OverlappingFieldsCanBeMergedRule,
+        UniqueInputFieldNamesRule,
+    ]
+)
+specified_rules.__doc__ = """\
+    This list includes all validation rules defined by the GraphQL spec.
+
+    The order of the rules in this list has been adjusted to lead to the
+    most clear output when encountering multiple validation errors.
+    """
+
+specified_sdl_rules: FrozenList[Type[ASTValidationRule]] = FrozenList(
+    [
+        LoneSchemaDefinitionRule,
+        UniqueOperationTypesRule,
+        UniqueTypeNamesRule,
+        UniqueEnumValueNamesRule,
+        UniqueFieldDefinitionNamesRule,
+        UniqueDirectiveNamesRule,
+        KnownTypeNamesRule,
+        KnownDirectivesRule,
+        UniqueDirectivesPerLocationRule,
+        PossibleTypeExtensionsRule,
+        KnownArgumentNamesOnDirectivesRule,
+        UniqueArgumentNamesRule,
+        UniqueInputFieldNamesRule,
+        ProvidedRequiredArgumentsOnDirectivesRule,
+    ]
+)
+specified_sdl_rules.__doc__ = """\
+    This list includes all rules for validating SDL.
+
+    For internal use only.
+    """
diff --git a/src/graphql/validation/validate.py b/src/graphql/validation/validate.py
new file mode 100644
index 0000000..fe4222f
--- /dev/null
+++ b/src/graphql/validation/validate.py
@@ -0,0 +1,128 @@
+from typing import Collection, List, Optional, Type
+
+from ..error import GraphQLError
+from ..language import DocumentNode, ParallelVisitor, visit
+from ..type import GraphQLSchema, assert_valid_schema
+from ..pyutils import inspect, is_collection
+from ..utilities import TypeInfo, TypeInfoVisitor
+from .rules import ASTValidationRule
+from .specified_rules import specified_rules, specified_sdl_rules
+from .validation_context import SDLValidationContext, ValidationContext
+
+__all__ = ["assert_valid_sdl", "assert_valid_sdl_extension", "validate", "validate_sdl"]
+
+
+class ValidationAbortedError(RuntimeError):
+    """Error when a validation has been aborted (error limit reached)."""
+
+
+def validate(
+    schema: GraphQLSchema,
+    document_ast: DocumentNode,
+    rules: Optional[Collection[Type[ASTValidationRule]]] = None,
+    type_info: Optional[TypeInfo] = None,
+    max_errors: Optional[int] = None,
+) -> List[GraphQLError]:
+    """Implements the "Validation" section of the spec.
+
+    Validation runs synchronously, returning a list of encountered errors, or an empty
+    list if no errors were encountered and the document is valid.
+
+    A list of specific validation rules may be provided. If not provided, the default
+    list of rules defined by the GraphQL specification will be used.
+
+    Each validation rule is a ValidationRule object which is a visitor object that holds
+    a ValidationContext (see the language/visitor API). Visitor methods are expected to
+    return GraphQLErrors, or lists of GraphQLErrors when invalid.
+
+    Optionally a custom TypeInfo instance may be provided. If not provided, one will be
+    created from the provided schema.
+    """
+    if not document_ast or not isinstance(document_ast, DocumentNode):
+        raise TypeError("Must provide document.")
+    # If the schema used for validation is invalid, throw an error.
+    assert_valid_schema(schema)
+    if type_info is None:
+        type_info = TypeInfo(schema)
+    elif not isinstance(type_info, TypeInfo):
+        raise TypeError(f"Not a TypeInfo object: {inspect(type_info)}.")
+    if rules is None:
+        rules = specified_rules
+    elif not is_collection(rules) or not all(
+        isinstance(rule, type) and issubclass(rule, ASTValidationRule) for rule in rules
+    ):
+        raise TypeError(
+            "Rules must be specified as a collection of ASTValidationRule subclasses."
+        )
+    if max_errors is not None and not isinstance(max_errors, int):
+        raise TypeError("The maximum number of errors must be passed as an int.")
+
+    errors: List[GraphQLError] = []
+
+    def on_error(error: GraphQLError) -> None:
+        if max_errors is not None and len(errors) >= max_errors:
+            errors.append(
+                GraphQLError(
+                    "Too many validation errors, error limit reached."
+                    " Validation aborted."
+                )
+            )
+            raise ValidationAbortedError
+        errors.append(error)
+
+    context = ValidationContext(schema, document_ast, type_info, on_error)
+
+    # This uses a specialized visitor which runs multiple visitors in parallel,
+    # while maintaining the visitor skip and break API.
+    visitors = [rule(context) for rule in rules]
+
+    # Visit the whole document with each instance of all provided rules.
+    try:
+        visit(document_ast, TypeInfoVisitor(type_info, ParallelVisitor(visitors)))
+    except ValidationAbortedError:
+        pass
+    return errors
+
+
+def validate_sdl(
+    document_ast: DocumentNode,
+    schema_to_extend: Optional[GraphQLSchema] = None,
+    rules: Optional[Collection[Type[ASTValidationRule]]] = None,
+) -> List[GraphQLError]:
+    """Validate an SDL document.
+
+    For internal use only.
+    """
+    errors: List[GraphQLError] = []
+    context = SDLValidationContext(document_ast, schema_to_extend, errors.append)
+    if rules is None:
+        rules = specified_sdl_rules
+    visitors = [rule(context) for rule in rules]
+    visit(document_ast, ParallelVisitor(visitors))
+    return errors
+
+
+def assert_valid_sdl(document_ast: DocumentNode) -> None:
+    """Assert document is valid SDL.
+
+    Utility function which asserts a SDL document is valid by throwing an error if it
+    is invalid.
+    """
+
+    errors = validate_sdl(document_ast)
+    if errors:
+        raise TypeError("\n\n".join(error.message for error in errors))
+
+
+def assert_valid_sdl_extension(
+    document_ast: DocumentNode, schema: GraphQLSchema
+) -> None:
+    """Assert document is a valid SDL extension.
+
+    Utility function which asserts a SDL document is valid by throwing an error if it
+    is invalid.
+    """
+
+    errors = validate_sdl(document_ast, schema)
+    if errors:
+        raise TypeError("\n\n".join(error.message for error in errors))
diff --git a/src/graphql/validation/validation_context.py b/src/graphql/validation/validation_context.py
new file mode 100644
index 0000000..1db7b45
--- /dev/null
+++ b/src/graphql/validation/validation_context.py
@@ -0,0 +1,243 @@
+from typing import Any, Callable, Dict, List, NamedTuple, Optional, Set, Union, cast
+
+from .. import GraphQLDirective
+from ..error import GraphQLError
+from ..language import (
+    DocumentNode,
+    FragmentDefinitionNode,
+    FragmentSpreadNode,
+    OperationDefinitionNode,
+    SelectionSetNode,
+    VariableNode,
+    Visitor,
+    VisitorAction,
+    visit,
+)
+from ..type import (
+    GraphQLArgument,
+    GraphQLCompositeType,
+    GraphQLField,
+    GraphQLInputType,
+    GraphQLOutputType,
+    GraphQLSchema,
+)
+from ..utilities import TypeInfo, TypeInfoVisitor
+
+__all__ = [
+    "ASTValidationContext",
+    "SDLValidationContext",
+    "ValidationContext",
+    "VariableUsage",
+    "VariableUsageVisitor",
+]
+
+NodeWithSelectionSet = Union[OperationDefinitionNode, FragmentDefinitionNode]
+
+
+class VariableUsage(NamedTuple):
+    node: VariableNode
+    type: Optional[GraphQLInputType]
+    default_value: Any
+
+
+class VariableUsageVisitor(Visitor):
+    """Visitor adding all variable usages to a given list."""
+
+    usages: List[VariableUsage]
+
+    def __init__(self, type_info: TypeInfo):
+        self.usages = []
+        self._append_usage = self.usages.append
+        self._type_info = type_info
+
+    def enter_variable_definition(self, *_args: Any) -> VisitorAction:
+        return self.SKIP
+
+    def enter_variable(self, node: VariableNode, *_args: Any) -> VisitorAction:
+        type_info = self._type_info
+        usage = VariableUsage(
+            node, type_info.get_input_type(), type_info.get_default_value()
+        )
+        self._append_usage(usage)
+        return None
+
+
+class ASTValidationContext:
+    """Utility class providing a context for validation of an AST.
+
+    An instance of this class is passed as the context attribute to all Validators,
+    allowing access to commonly useful contextual information from within a validation
+    rule.
+    """
+
+    document: DocumentNode
+
+    _fragments: Optional[Dict[str, FragmentDefinitionNode]]
+    _fragment_spreads: Dict[SelectionSetNode, List[FragmentSpreadNode]]
+    _recursively_referenced_fragments: Dict[
+        OperationDefinitionNode, List[FragmentDefinitionNode]
+    ]
+
+    def __init__(
+        self, ast: DocumentNode, on_error: Callable[[GraphQLError], None]
+    ) -> None:
+        self.document = ast
+        self.on_error = on_error  # type: ignore
+        self._fragments = None
+        self._fragment_spreads = {}
+        self._recursively_referenced_fragments = {}
+
+    def on_error(self, error: GraphQLError) -> None:
+        pass
+
+    def report_error(self, error: GraphQLError) -> None:
+        self.on_error(error)
+
+    def get_fragment(self, name: str) -> Optional[FragmentDefinitionNode]:
+        fragments = self._fragments
+        if fragments is None:
+            fragments = {}
+            for statement in self.document.definitions:
+                if isinstance(statement, FragmentDefinitionNode):
+                    fragments[statement.name.value] = statement
+            self._fragments = fragments
+        return fragments.get(name)
+
+    def get_fragment_spreads(self, node: SelectionSetNode) -> List[FragmentSpreadNode]:
+        spreads = self._fragment_spreads.get(node)
+        if spreads is None:
+            spreads = []
+            append_spread = spreads.append
+            sets_to_visit = [node]
+            append_set = sets_to_visit.append
+            pop_set = sets_to_visit.pop
+            while sets_to_visit:
+                visited_set = pop_set()
+                for selection in visited_set.selections:
+                    if isinstance(selection, FragmentSpreadNode):
+                        append_spread(selection)
+                    else:
+                        set_to_visit = cast(
+                            NodeWithSelectionSet, selection
+                        ).selection_set
+                        if set_to_visit:
+                            append_set(set_to_visit)
+            self._fragment_spreads[node] = spreads
+        return spreads
+
+    def get_recursively_referenced_fragments(
+        self, operation: OperationDefinitionNode
+    ) -> List[FragmentDefinitionNode]:
+        fragments = self._recursively_referenced_fragments.get(operation)
+        if fragments is None:
+            fragments = []
+            append_fragment = fragments.append
+            collected_names: Set[str] = set()
+            add_name = collected_names.add
+            nodes_to_visit = [operation.selection_set]
+            append_node = nodes_to_visit.append
+            pop_node = nodes_to_visit.pop
+            get_fragment = self.get_fragment
+            get_fragment_spreads = self.get_fragment_spreads
+            while nodes_to_visit:
+                visited_node = pop_node()
+                for spread in get_fragment_spreads(visited_node):
+                    frag_name = spread.name.value
+                    if frag_name not in collected_names:
+                        add_name(frag_name)
+                        fragment = get_fragment(frag_name)
+                        if fragment:
+                            append_fragment(fragment)
+                            append_node(fragment.selection_set)
+            self._recursively_referenced_fragments[operation] = fragments
+        return fragments
+
+
+class SDLValidationContext(ASTValidationContext):
+    """Utility class providing a context for validation of an SDL AST.
+
+    An instance of this class is passed as the context attribute to all Validators,
+    allowing access to commonly useful contextual information from within a validation
+    rule.
+    """
+
+    schema: Optional[GraphQLSchema]
+
+    def __init__(
+        self,
+        ast: DocumentNode,
+        schema: Optional[GraphQLSchema],
+        on_error: Callable[[GraphQLError], None],
+    ) -> None:
+        super().__init__(ast, on_error)
+        self.schema = schema
+
+
+class ValidationContext(ASTValidationContext):
+    """Utility class providing a context for validation using a GraphQL schema.
+
+    An instance of this class is passed as the context attribute to all Validators,
+    allowing access to commonly useful contextual information from within a validation
+    rule.
+    """
+
+    schema: GraphQLSchema
+
+    _type_info: TypeInfo
+    _variable_usages: Dict[NodeWithSelectionSet, List[VariableUsage]]
+    _recursive_variable_usages: Dict[OperationDefinitionNode, List[VariableUsage]]
+
+    def __init__(
+        self,
+        schema: GraphQLSchema,
+        ast: DocumentNode,
+        type_info: TypeInfo,
+        on_error: Callable[[GraphQLError], None],
+    ) -> None:
+        super().__init__(ast, on_error)
+        self.schema = schema
+        self._type_info = type_info
+        self._variable_usages = {}
+        self._recursive_variable_usages = {}
+
+    def get_variable_usages(self, node: NodeWithSelectionSet) -> List[VariableUsage]:
+        usages = self._variable_usages.get(node)
+        if usages is None:
+            usage_visitor = VariableUsageVisitor(self._type_info)
+            visit(node, TypeInfoVisitor(self._type_info, usage_visitor))
+            usages = usage_visitor.usages
+            self._variable_usages[node] = usages
+        return usages
+
+    def get_recursive_variable_usages(
+        self, operation: OperationDefinitionNode
+    ) -> List[VariableUsage]:
+        usages = self._recursive_variable_usages.get(operation)
+        if usages is None:
+            get_variable_usages = self.get_variable_usages
+            usages = get_variable_usages(operation)
+            for fragment in self.get_recursively_referenced_fragments(operation):
+                usages.extend(get_variable_usages(fragment))
+            self._recursive_variable_usages[operation] = usages
+        return usages
+
+    def get_type(self) -> Optional[GraphQLOutputType]:
+        return self._type_info.get_type()
+
+    def get_parent_type(self) -> Optional[GraphQLCompositeType]:
+        return self._type_info.get_parent_type()
+
+    def get_input_type(self) -> Optional[GraphQLInputType]:
+        return self._type_info.get_input_type()
+
+    def get_parent_input_type(self) -> Optional[GraphQLInputType]:
+        return self._type_info.get_parent_input_type()
+
+    def get_field_def(self) -> Optional[GraphQLField]:
+        return self._type_info.get_field_def()
+
+    def get_directive(self) -> Optional[GraphQLDirective]:
+        return self._type_info.get_directive()
+
+    def get_argument(self) -> Optional[GraphQLArgument]:
+        return self._type_info.get_argument()
diff --git a/src/graphql/version.py b/src/graphql/version.py
new file mode 100644
index 0000000..509ae75
--- /dev/null
+++ b/src/graphql/version.py
@@ -0,0 +1,49 @@
+import re
+from typing import NamedTuple
+
+__all__ = ["version", "version_info", "version_js", "version_info_js"]
+
+
+version = "3.1.2"
+
+version_js = "15.1.0"
+
+
+_re_version = re.compile(r"(\d+)\.(\d+)\.(\d+)(\D*)(\d*)")
+
+
+class VersionInfo(NamedTuple):
+    major: int
+    minor: int
+    micro: int
+    releaselevel: str
+    serial: int
+
+    @classmethod
+    def from_str(cls, v: str) -> "VersionInfo":
+        groups = _re_version.match(v).groups()  # type: ignore
+        major, minor, micro = map(int, groups[:3])
+        level = (groups[3] or "")[:1]
+        if level == "a":
+            level = "alpha"
+        elif level == "b":
+            level = "beta"
+        elif level in ("c", "r"):
+            level = "candidate"
+        else:
+            level = "final"
+        serial = groups[4]
+        serial = int(serial) if serial else 0
+        return cls(major, minor, micro, level, serial)
+
+    def __str__(self) -> str:
+        v = f"{self.major}.{self.minor}.{self.micro}"
+        level = self.releaselevel
+        if level and level != "final":
+            v = f"{v}{level[:1]}{self.serial}"
+        return v
+
+
+version_info = VersionInfo.from_str(version)
+
+version_info_js = VersionInfo.from_str(version_js)
diff --git a/tests/__init__.py b/tests/__init__.py
index e69de29..db4fe36 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -0,0 +1 @@
+"""Tests for graphql"""
diff --git a/tests/benchmarks/__init__.py b/tests/benchmarks/__init__.py
new file mode 100644
index 0000000..a1156e8
--- /dev/null
+++ b/tests/benchmarks/__init__.py
@@ -0,0 +1,9 @@
+"""Benchmarks for graphql
+
+Benchmarks are disabled (only executed as tests) by default in setup.cfg.
+You can enable them with --benchmark-enable if your want to execute them.
+
+E.g. in order to execute all the benchmarks with tox using Python 3.7::
+
+    tox -e py37 -- -k benchmarks --benchmark-enable
+"""
diff --git a/tests/benchmarks/test_build_ast_schema.py b/tests/benchmarks/test_build_ast_schema.py
new file mode 100644
index 0000000..b41626f
--- /dev/null
+++ b/tests/benchmarks/test_build_ast_schema.py
@@ -0,0 +1,11 @@
+from graphql import parse, build_ast_schema, GraphQLSchema
+
+from ..fixtures import big_schema_sdl  # noqa: F401
+
+
+def test_build_schema_from_ast(benchmark, big_schema_sdl):  # noqa: F811
+    schema_ast = parse(big_schema_sdl)
+    schema: GraphQLSchema = benchmark(
+        lambda: build_ast_schema(schema_ast, assume_valid=True)
+    )
+    assert schema.query_type is not None
diff --git a/tests/benchmarks/test_build_client_schema.py b/tests/benchmarks/test_build_client_schema.py
new file mode 100644
index 0000000..a8627f8
--- /dev/null
+++ b/tests/benchmarks/test_build_client_schema.py
@@ -0,0 +1,14 @@
+from graphql import build_client_schema, GraphQLSchema
+
+from ..fixtures import big_schema_introspection_result  # noqa: F401
+
+
+def test_build_schema_from_introspection(
+    benchmark, big_schema_introspection_result  # noqa: F811
+):
+    schema: GraphQLSchema = benchmark(
+        lambda: build_client_schema(
+            big_schema_introspection_result["data"], assume_valid=True
+        )
+    )
+    assert schema.query_type is not None
diff --git a/tests/benchmarks/test_introspection_from_schema.py b/tests/benchmarks/test_introspection_from_schema.py
new file mode 100644
index 0000000..116f6f4
--- /dev/null
+++ b/tests/benchmarks/test_introspection_from_schema.py
@@ -0,0 +1,11 @@
+from graphql import build_schema, parse, execute
+from graphql.utilities import get_introspection_query
+
+from ..fixtures import big_schema_sdl  # noqa: F401
+
+
+def test_execute_introspection_query(benchmark, big_schema_sdl):  # noqa: F811
+    schema = build_schema(big_schema_sdl, assume_valid=True)
+    document = parse(get_introspection_query())
+    result = benchmark(lambda: execute(schema=schema, document=document))
+    assert result.errors is None
diff --git a/tests/benchmarks/test_parser.py b/tests/benchmarks/test_parser.py
new file mode 100644
index 0000000..7d059f2
--- /dev/null
+++ b/tests/benchmarks/test_parser.py
@@ -0,0 +1,8 @@
+from graphql import parse, DocumentNode
+
+from ..fixtures import kitchen_sink_query  # noqa: F401
+
+
+def test_parse_kitchen_sink(benchmark, kitchen_sink_query):  # noqa: F811
+    query = benchmark(lambda: parse(kitchen_sink_query))
+    assert isinstance(query, DocumentNode)
diff --git a/tests/benchmarks/test_validate_gql.py b/tests/benchmarks/test_validate_gql.py
new file mode 100644
index 0000000..addae69
--- /dev/null
+++ b/tests/benchmarks/test_validate_gql.py
@@ -0,0 +1,11 @@
+from graphql import build_schema, parse, validate
+from graphql.utilities import get_introspection_query
+
+from ..fixtures import big_schema_sdl  # noqa: F401
+
+
+def test_validate_introspection_query(benchmark, big_schema_sdl):  # noqa: F811
+    schema = build_schema(big_schema_sdl, assume_valid=True)
+    query = parse(get_introspection_query())
+    result = benchmark(lambda: validate(schema, query))
+    assert result == []
diff --git a/tests/benchmarks/test_validate_invalid_gql.py b/tests/benchmarks/test_validate_invalid_gql.py
new file mode 100644
index 0000000..dec9bbc
--- /dev/null
+++ b/tests/benchmarks/test_validate_invalid_gql.py
@@ -0,0 +1,33 @@
+from graphql import build_schema, parse, validate
+
+from ..fixtures import big_schema_sdl  # noqa: F401
+
+
+def test_validate_invalid_query(benchmark, big_schema_sdl):  # noqa: F811
+    schema = build_schema(big_schema_sdl, assume_valid=True)
+    query_ast = parse(
+        """
+        {
+          unknownField
+          ... on unknownType {
+            anotherUnknownField
+            ...unknownFragment
+          }
+        }
+
+        fragment TestFragment on anotherUnknownType {
+          yetAnotherUnknownField
+        }
+        """
+    )
+    result = benchmark(lambda: validate(schema, query_ast))
+    assert result == [
+        {
+            "message": "Cannot query field 'unknownField' on type 'Query'.",
+            "locations": [(3, 11)],
+        },
+        {"message": "Unknown type 'unknownType'.", "locations": [(4, 18)]},
+        {"message": "Unknown fragment 'unknownFragment'.", "locations": [(6, 16)]},
+        {"message": "Unknown type 'anotherUnknownType'.", "locations": [(10, 34)]},
+        {"message": "Fragment 'TestFragment' is never used.", "locations": [(10, 9)]},
+    ]
diff --git a/tests/benchmarks/test_validate_sdl.py b/tests/benchmarks/test_validate_sdl.py
new file mode 100644
index 0000000..7d48fc4
--- /dev/null
+++ b/tests/benchmarks/test_validate_sdl.py
@@ -0,0 +1,10 @@
+from graphql import parse
+from graphql.validation.validate import validate_sdl
+
+from ..fixtures import big_schema_sdl  # noqa: F401
+
+
+def test_validate_sdl_document(benchmark, big_schema_sdl):  # noqa: F811
+    sdl_ast = parse(big_schema_sdl)
+    result = benchmark(lambda: validate_sdl(sdl_ast))
+    assert result == []
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000..bf88536
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,22 @@
+# pytest configuration
+
+import pytest  # type: ignore
+
+
+def pytest_addoption(parser):
+    parser.addoption(
+        "--run-slow", action="store_true", default=False, help="run slow tests"
+    )
+
+
+def pytest_configure(config):
+    config.addinivalue_line("markers", "slow: mark test as slow to run")
+
+
+def pytest_collection_modifyitems(config, items):
+    if not config.getoption("--run-slow"):
+        # without --run-slow option: skip all slow tests
+        skip_slow = pytest.mark.skip(reason="need --run-slow option to run")
+        for item in items:
+            if "slow" in item.keywords:
+                item.add_marker(skip_slow)
diff --git a/tests/error/__init__.py b/tests/error/__init__.py
new file mode 100644
index 0000000..c93fa18
--- /dev/null
+++ b/tests/error/__init__.py
@@ -0,0 +1 @@
+"""Tests for graphql.error"""
diff --git a/tests/error/test_format_error.py b/tests/error/test_format_error.py
new file mode 100644
index 0000000..bb65a80
--- /dev/null
+++ b/tests/error/test_format_error.py
@@ -0,0 +1,75 @@
+from typing import List, Union
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError, format_error
+from graphql.language import Node, Source
+from graphql.pyutils import Undefined
+
+
+def describe_format_error():
+    def formats_graphql_error():
+        source = Source(
+            """
+            query {
+              something
+            }"""
+        )
+        path: List[Union[int, str]] = ["one", 2]
+        extensions = {"ext": None}
+        error = GraphQLError(
+            "test message",
+            Node(),
+            source,
+            [14, 40],
+            path,
+            ValueError("original"),
+            extensions=extensions,
+        )
+        formatted = format_error(error)
+        assert formatted == error.formatted
+        assert formatted == {
+            "message": "test message",
+            "locations": [{"line": 2, "column": 14}, {"line": 3, "column": 20}],
+            "path": path,
+            "extensions": extensions,
+        }
+
+    def uses_default_message():
+        # noinspection PyTypeChecker
+        formatted = format_error(GraphQLError(None))  # type: ignore
+
+        assert formatted == {
+            "message": "An unknown error occurred.",
+            "locations": None,
+            "path": None,
+        }
+
+    def includes_path():
+        path: List[Union[int, str]] = ["path", 3, "to", "field"]
+        error = GraphQLError("msg", path=path)
+        formatted = format_error(error)
+        assert formatted == error.formatted
+        assert formatted == {"message": "msg", "locations": None, "path": path}
+
+    def includes_extension_fields():
+        error = GraphQLError("msg", extensions={"foo": "bar"})
+        formatted = format_error(error)
+        assert formatted == error.formatted
+        assert formatted == {
+            "message": "msg",
+            "locations": None,
+            "path": None,
+            "extensions": {"foo": "bar"},
+        }
+
+    def rejects_none_and_undefined_errors():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            format_error(None)  # type: ignore
+        assert str(exc_info.value) == "Expected a GraphQLError."
+
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            format_error(Undefined)  # type: ignore
+        assert str(exc_info.value) == "Expected a GraphQLError."
diff --git a/tests/error/test_graphql_error.py b/tests/error/test_graphql_error.py
new file mode 100644
index 0000000..3030fba
--- /dev/null
+++ b/tests/error/test_graphql_error.py
@@ -0,0 +1,242 @@
+from typing import cast, List, Union
+
+from graphql.error import GraphQLError, print_error
+from graphql.language import (
+    parse,
+    OperationDefinitionNode,
+    ObjectTypeDefinitionNode,
+    Source,
+)
+
+from ..utils import dedent
+
+
+source = Source(
+    dedent(
+        """
+    {
+      field
+    }
+    """
+    )
+)
+
+ast = parse(source)
+operation_node = ast.definitions[0]
+operation_node = cast(OperationDefinitionNode, operation_node)
+assert operation_node and operation_node.kind == "operation_definition"
+field_node = operation_node.selection_set.selections[0]
+assert field_node
+
+
+def describe_graphql_error():
+    def is_a_class_and_is_a_subclass_of_exception():
+        assert type(GraphQLError) is type
+        assert issubclass(GraphQLError, Exception)
+        assert isinstance(GraphQLError("str"), Exception)
+        assert isinstance(GraphQLError("str"), GraphQLError)
+
+    def has_a_name_message_and_stack_trace():
+        e = GraphQLError("msg")
+        assert e.__class__.__name__ == "GraphQLError"
+        assert e.message == "msg"
+
+    def uses_the_stack_of_an_original_error():
+        try:
+            raise RuntimeError("original")
+        except RuntimeError as runtime_error:
+            original = runtime_error
+        e = GraphQLError("msg", original_error=original)
+        assert e.__class__.__name__ == "GraphQLError"
+        assert e.__traceback__ is original.__traceback__
+        assert e.message == "msg"
+        assert e.original_error is original
+        assert str(e.original_error) == "original"
+
+    def passes_the_context_of_an_original_error():
+        context = ValueError("cause")
+        try:
+            raise context
+        except ValueError:
+            try:
+                raise RuntimeError("effect")
+            except RuntimeError as runtime_error:
+                original = runtime_error
+        e = GraphQLError("msg", original_error=original)
+        assert e.__context__ is context
+
+    def passes_the_cause_of_an_original_error():
+        cause = ValueError("cause")
+        try:
+            raise RuntimeError("effect") from cause
+        except RuntimeError as runtime_error:
+            original = runtime_error
+        e = GraphQLError("msg", original_error=original)
+        assert e.__cause__ is cause
+
+    def creates_new_stack_if_original_error_has_no_stack():
+        try:
+            raise RuntimeError
+        except RuntimeError as original_with_traceback:
+            original_traceback = original_with_traceback.__traceback__
+            original = RuntimeError("original")
+            e = GraphQLError("msg", original_error=original)
+        assert e.__class__.__name__ == "GraphQLError"
+        assert original.__traceback__ is None
+        assert original_traceback is not None
+        assert e.__traceback__ is original_traceback
+        assert e.message == "msg"
+        assert e.original_error is original
+        assert str(e.original_error) == "original"
+
+    def converts_nodes_to_positions_and_locations():
+        e = GraphQLError("msg", [field_node])
+        assert e.nodes == [field_node]
+        assert e.source is source
+        assert e.positions == [4]
+        assert e.locations == [(2, 3)]
+
+    def converts_single_node_to_positions_and_locations():
+        e = GraphQLError("msg", field_node)  # Non-array value.
+        assert e.nodes == [field_node]
+        assert e.source is source
+        assert e.positions == [4]
+        assert e.locations == [(2, 3)]
+
+    def converts_node_with_loc_start_zero_to_positions_and_locations():
+        e = GraphQLError("msg", operation_node)
+        assert e.nodes == [operation_node]
+        assert e.source is source
+        assert e.positions == [0]
+        assert e.locations == [(1, 1)]
+
+    def converts_source_and_positions_to_locations():
+        e = GraphQLError("msg", None, source, [6])
+        assert e.nodes is None
+        assert e.source is source
+        assert e.positions == [6]
+        assert e.locations == [(2, 5)]
+
+    def serializes_to_include_message():
+        e = GraphQLError("msg")
+        assert str(e) == "msg"
+        assert repr(e) == "GraphQLError('msg')"
+
+    def serializes_to_include_message_and_locations():
+        e = GraphQLError("msg", field_node)
+        assert "msg" in str(e)
+        assert ":2:3" in str(e)
+        assert repr(e) == (
+            "GraphQLError('msg', locations=[SourceLocation(line=2, column=3)])"
+        )
+
+    def repr_includes_extensions():
+        e = GraphQLError("msg", extensions={"foo": "bar"})
+        assert repr(e) == ("GraphQLError('msg', extensions={'foo': 'bar'})")
+
+    def serializes_to_include_path():
+        path: List[Union[int, str]] = ["path", 3, "to", "field"]
+        e = GraphQLError("msg", path=path)
+        assert e.path is path
+        assert repr(e) == "GraphQLError('msg', path=['path', 3, 'to', 'field'])"
+
+    def always_stores_path_as_list():
+        path: List[Union[int, str]] = ["path", 3, "to", "field"]
+        e = GraphQLError("msg,", path=tuple(path))
+        assert isinstance(e.path, list)
+        assert e.path == path
+
+    def is_comparable():
+        e1 = GraphQLError("msg,", path=["field", 1])
+        assert e1 == e1
+        assert e1 == e1.formatted
+        assert not e1 != e1
+        assert not e1 != e1.formatted
+        e2 = GraphQLError("msg,", path=["field", 1])
+        assert e1 == e2
+        assert not e1 != e2
+        assert e2.path and e2.path[1] == 1
+        e2.path[1] = 2
+        assert not e1 == e2
+        assert e1 != e2
+        assert not e1 == e2.formatted
+        assert e1 != e2.formatted
+
+    def is_hashable():
+        hash(GraphQLError("msg"))
+
+    def hashes_are_unique_per_instance():
+        e1 = GraphQLError("msg")
+        e2 = GraphQLError("msg")
+        assert hash(e1) != hash(e2)
+
+
+def describe_print_error():
+    def prints_an_error_without_location():
+        error = GraphQLError("Error without location")
+        assert print_error(error) == "Error without location"
+
+    def prints_an_error_using_node_without_location():
+        error = GraphQLError(
+            "Error attached to node without location",
+            parse("{ foo }", no_location=True),
+        )
+        assert print_error(error) == "Error attached to node without location"
+
+    def prints_an_error_with_nodes_from_different_sources():
+        doc_a = parse(
+            Source(
+                dedent(
+                    """
+                    type Foo {
+                      field: String
+                    }
+                    """
+                ),
+                "SourceA",
+            )
+        )
+        op_a = doc_a.definitions[0]
+        op_a = cast(ObjectTypeDefinitionNode, op_a)
+        assert op_a and op_a.kind == "object_type_definition" and op_a.fields
+        field_a = op_a.fields[0]
+        doc_b = parse(
+            Source(
+                dedent(
+                    """
+                    type Foo {
+                      field: Int
+                    }
+                    """
+                ),
+                "SourceB",
+            )
+        )
+        op_b = doc_b.definitions[0]
+        op_b = cast(ObjectTypeDefinitionNode, op_b)
+        assert op_b and op_b.kind == "object_type_definition" and op_b.fields
+        field_b = op_b.fields[0]
+
+        error = GraphQLError(
+            "Example error with two nodes", [field_a.type, field_b.type]
+        )
+
+        printed_error = print_error(error)
+        assert printed_error + "\n" == dedent(
+            """
+            Example error with two nodes
+
+            SourceA:2:10
+            1 | type Foo {
+            2 |   field: String
+              |          ^
+            3 | }
+
+            SourceB:2:10
+            1 | type Foo {
+            2 |   field: Int
+              |          ^
+            3 | }
+            """
+        )
+        assert str(error) == printed_error
diff --git a/tests/error/test_located_error.py b/tests/error/test_located_error.py
new file mode 100644
index 0000000..e4039ed
--- /dev/null
+++ b/tests/error/test_located_error.py
@@ -0,0 +1,28 @@
+from typing import cast, Any
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError, located_error
+
+
+def describe_located_error():
+    def throws_without_an_original_error():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            located_error([], [], [])  # type: ignore
+        assert str(exc_info.value) == "Expected an Exception."
+
+    def passes_graphql_error_through():
+        path = ["path", 3, "to", "field"]
+        e = GraphQLError("msg", None, None, None, cast(Any, path))
+        assert located_error(e, [], []) == e
+
+    def passes_graphql_error_ish_through():
+        e = GraphQLError("I am a located GraphQL error")
+        e.path = []
+        assert located_error(e, [], []) is e
+
+    def does_not_pass_through_elasticsearch_like_errors():
+        e = Exception("I am from elasticsearch")
+        cast(Any, e).path = "/something/feed/_search"
+        assert located_error(e, [], []) is not e
diff --git a/tests/error/test_print_location.py b/tests/error/test_print_location.py
new file mode 100644
index 0000000..1141248
--- /dev/null
+++ b/tests/error/test_print_location.py
@@ -0,0 +1,81 @@
+from graphql.language import print_source_location, Source, SourceLocation
+
+from ..utils import dedent
+
+
+def describe_print_location():
+    def prints_minified_documents():
+        minified_source = Source(
+            "query SomeMinifiedQueryWithErrorInside("
+            "$foo:String!=FIRST_ERROR_HERE$bar:String)"
+            "{someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE)"
+            "{fieldA fieldB{fieldC fieldD...on THIRD_ERROR_HERE}}}"
+        )
+
+        first_location = print_source_location(
+            minified_source,
+            SourceLocation(1, minified_source.body.index("FIRST_ERROR_HERE") + 1),
+        )
+        assert first_location + "\n" == dedent(
+            """
+            GraphQL request:1:53
+            1 | query SomeMinifiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String)
+              |                                                     ^
+              | {someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD.
+            """  # noqa: E501
+        )
+
+        second_location = print_source_location(
+            minified_source,
+            SourceLocation(1, minified_source.body.index("SECOND_ERROR_HERE") + 1),
+        )
+        assert second_location + "\n" == dedent(
+            """
+            GraphQL request:1:114
+            1 | query SomeMinifiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String)
+              | {someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD.
+              |                                  ^
+              | ..on THIRD_ERROR_HERE}}}
+            """  # noqa: E501
+        )
+
+        third_location = print_source_location(
+            minified_source,
+            SourceLocation(1, minified_source.body.index("THIRD_ERROR_HERE") + 1),
+        )
+        assert third_location + "\n" == dedent(
+            """
+            GraphQL request:1:166
+            1 | query SomeMinifiedQueryWithErrorInside($foo:String!=FIRST_ERROR_HERE$bar:String)
+              | {someField(foo:$foo bar:$bar baz:SECOND_ERROR_HERE){fieldA fieldB{fieldC fieldD.
+              | ..on THIRD_ERROR_HERE}}}
+              |      ^
+            """  # noqa: E501
+        )
+
+    def prints_single_digit_line_number_with_no_padding():
+        result = print_source_location(
+            Source("*", "Test", SourceLocation(9, 1)), SourceLocation(1, 1)
+        )
+
+        assert result + "\n" == dedent(
+            """
+            Test:9:1
+            9 | *
+              | ^
+            """
+        )
+
+    def prints_line_numbers_with_correct_padding():
+        result = print_source_location(
+            Source("*\n", "Test", SourceLocation(9, 1)), SourceLocation(1, 1)
+        )
+
+        assert result + "\n" == dedent(
+            """
+            Test:9:1
+             9 | *
+               | ^
+            10 |
+            """
+        )
diff --git a/tests/execution/__init__.py b/tests/execution/__init__.py
new file mode 100644
index 0000000..39aad3e
--- /dev/null
+++ b/tests/execution/__init__.py
@@ -0,0 +1 @@
+"""Tests for graphql.execution"""
diff --git a/tests/execution/test_abstract.py b/tests/execution/test_abstract.py
new file mode 100644
index 0000000..0004164
--- /dev/null
+++ b/tests/execution/test_abstract.py
@@ -0,0 +1,471 @@
+from typing import NamedTuple
+
+from graphql import graphql_sync
+from graphql.error import format_error
+from graphql.type import (
+    GraphQLBoolean,
+    GraphQLField,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+    GraphQLUnionType,
+)
+
+
+class Dog(NamedTuple):
+
+    name: str
+    woofs: bool
+
+
+class Cat(NamedTuple):
+
+    name: str
+    meows: bool
+
+
+class Human(NamedTuple):
+
+    name: str
+
+
+def get_is_type_of(type_):
+    def is_type_of(obj, _info):
+        return isinstance(obj, type_)
+
+    return is_type_of
+
+
+def get_type_resolver(types):
+    def resolve(obj, _info, _type):
+        return resolve_thunk(types)[obj.__class__]
+
+    return resolve
+
+
+def resolve_thunk(thunk):
+    return thunk() if callable(thunk) else thunk
+
+
+def describe_execute_handles_synchronous_execution_of_abstract_types():
+    def is_type_of_used_to_resolve_runtime_type_for_interface():
+        PetType = GraphQLInterfaceType("Pet", {"name": GraphQLField(GraphQLString)})
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+            is_type_of=get_is_type_of(Dog),
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+            is_type_of=get_is_type_of(Cat),
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_args: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                        ],
+                    )
+                },
+            ),
+            types=[CatType, DogType],
+        )
+
+        query = """
+            {
+              pets {
+                name
+                ... on Dog {
+                  woofs
+                }
+                ... on Cat {
+                  meows
+                }
+              }
+            }
+            """
+
+        result = graphql_sync(schema, query)
+        assert result == (
+            {
+                "pets": [
+                    {"name": "Odie", "woofs": True},
+                    {"name": "Garfield", "meows": False},
+                ]
+            },
+            None,
+        )
+
+    def is_type_of_used_to_resolve_runtime_type_for_union():
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            is_type_of=get_is_type_of(Dog),
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            is_type_of=get_is_type_of(Cat),
+        )
+
+        PetType = GraphQLUnionType("Pet", [CatType, DogType])
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_args: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                        ],
+                    )
+                },
+            )
+        )
+
+        query = """
+            {
+              pets {
+                ... on Dog {
+                  name
+                  woofs
+                }
+                ... on Cat {
+                  name
+                  meows
+                }
+              }
+            }
+            """
+
+        result = graphql_sync(schema, query)
+        assert result == (
+            {
+                "pets": [
+                    {"name": "Odie", "woofs": True},
+                    {"name": "Garfield", "meows": False},
+                ]
+            },
+            None,
+        )
+
+    def resolve_type_on_interface_yields_useful_error():
+        CatType: GraphQLObjectType
+        DogType: GraphQLObjectType
+        HumanType: GraphQLObjectType
+
+        PetType = GraphQLInterfaceType(
+            "Pet",
+            {"name": GraphQLField(GraphQLString)},
+            resolve_type=get_type_resolver(
+                lambda: {Dog: DogType, Cat: CatType, Human: HumanType}
+            ),
+        )
+
+        HumanType = GraphQLObjectType("Human", {"name": GraphQLField(GraphQLString)})
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_args: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                            Human("Jon"),
+                        ],
+                    )
+                },
+            ),
+            types=[CatType, DogType],
+        )
+
+        query = """
+            {
+              pets {
+                name
+                ... on Dog {
+                  woofs
+                }
+                ... on Cat {
+                  meows
+                }
+              }
+            }
+            """
+
+        result = graphql_sync(schema, query)
+        assert result.data == {
+            "pets": [
+                {"name": "Odie", "woofs": True},
+                {"name": "Garfield", "meows": False},
+                None,
+            ]
+        }
+
+        assert result.errors
+        assert len(result.errors) == 1
+        assert format_error(result.errors[0]) == {
+            "message": "Runtime Object type 'Human'"
+            " is not a possible type for 'Pet'.",
+            "locations": [{"line": 3, "column": 15}],
+            "path": ["pets", 2],
+        }
+
+    def resolve_type_on_union_yields_useful_error():
+        HumanType = GraphQLObjectType("Human", {"name": GraphQLField(GraphQLString)})
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+        )
+
+        PetType = GraphQLUnionType(
+            "Pet",
+            [DogType, CatType],
+            resolve_type=get_type_resolver(
+                {Dog: DogType, Cat: CatType, Human: HumanType}
+            ),
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                            Human("Jon"),
+                        ],
+                    )
+                },
+            )
+        )
+
+        query = """
+            {
+              pets {
+                ... on Dog {
+                  name
+                  woofs
+                }
+                ... on Cat {
+                  name
+                  meows
+                }
+              }
+            }
+            """
+
+        result = graphql_sync(schema, query)
+        assert result.data == {
+            "pets": [
+                {"name": "Odie", "woofs": True},
+                {"name": "Garfield", "meows": False},
+                None,
+            ]
+        }
+
+        assert result.errors
+        assert len(result.errors) == 1
+        assert format_error(result.errors[0]) == {
+            "message": "Runtime Object type 'Human'"
+            " is not a possible type for 'Pet'.",
+            "locations": [{"line": 3, "column": 15}],
+            "path": ["pets", 2],
+        }
+
+    def returning_invalid_value_from_resolve_type_yields_useful_error():
+        foo_interface = GraphQLInterfaceType(
+            "FooInterface",
+            {"bar": GraphQLField(GraphQLString)},
+            resolve_type=lambda *_args: [],  # type: ignore
+        )
+
+        foo_object = GraphQLObjectType(
+            "FooObject",
+            {"bar": GraphQLField(GraphQLString)},
+            interfaces=[foo_interface],
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {"foo": GraphQLField(foo_interface, resolve=lambda *_args: "dummy")},
+            ),
+            types=[foo_object],
+        )
+
+        result = graphql_sync(schema, "{ foo { bar } }")
+
+        assert result == (
+            {"foo": None},
+            [
+                {
+                    "message": "Abstract type 'FooInterface' must resolve to an"
+                    " Object type at runtime for field 'Query.foo' with value 'dummy',"
+                    " received '[]'. Either the 'FooInterface' type should provide"
+                    " a 'resolve_type' function or each possible type"
+                    " should provide an 'is_type_of' function.",
+                    "locations": [(1, 3)],
+                    "path": ["foo"],
+                }
+            ],
+        )
+
+    def missing_both_resolve_type_and_is_type_of_yields_useful_error():
+        foo_interface = GraphQLInterfaceType(
+            "FooInterface", {"bar": GraphQLField(GraphQLString)}
+        )
+
+        foo_object = GraphQLObjectType(
+            "FooObject",
+            {"bar": GraphQLField(GraphQLString)},
+            interfaces=[foo_interface],
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {"foo": GraphQLField(foo_interface, resolve=lambda *_args: "dummy")},
+            ),
+            types=[foo_object],
+        )
+
+        result = graphql_sync(schema, "{ foo { bar } }")
+
+        assert result == (
+            {"foo": None},
+            [
+                {
+                    "message": "Abstract type 'FooInterface' must resolve to an"
+                    " Object type at runtime for field 'Query.foo' with value 'dummy',"
+                    " received 'None'. Either the 'FooInterface' type should provide"
+                    " a 'resolve_type' function or each possible type"
+                    " should provide an 'is_type_of' function.",
+                    "locations": [(1, 3)],
+                    "path": ["foo"],
+                }
+            ],
+        )
+
+    def resolve_type_allows_resolving_with_type_name():
+        PetType = GraphQLInterfaceType(
+            "Pet",
+            {"name": GraphQLField(GraphQLString)},
+            resolve_type=get_type_resolver({Dog: "Dog", Cat: "Cat"}),
+        )
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_: [Dog("Odie", True), Cat("Garfield", False)],
+                    )
+                },
+            ),
+            types=[CatType, DogType],
+        )
+
+        query = """
+            {
+              pets {
+                name
+                ... on Dog {
+                  woofs
+                }
+                ... on Cat {
+                  meows
+                }
+              }
+            }"""
+
+        result = graphql_sync(schema, query)
+        assert result == (
+            {
+                "pets": [
+                    {"name": "Odie", "woofs": True},
+                    {"name": "Garfield", "meows": False},
+                ]
+            },
+            None,
+        )
diff --git a/tests/execution/test_abstract_async.py b/tests/execution/test_abstract_async.py
new file mode 100644
index 0000000..1378aa7
--- /dev/null
+++ b/tests/execution/test_abstract_async.py
@@ -0,0 +1,597 @@
+from typing import NamedTuple
+
+from pytest import mark  # type: ignore
+
+from graphql import graphql
+from graphql.type import (
+    GraphQLBoolean,
+    GraphQLField,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+    GraphQLUnionType,
+)
+
+
+class Dog(NamedTuple):
+
+    name: str
+    woofs: bool
+
+
+class Cat(NamedTuple):
+
+    name: str
+    meows: bool
+
+
+class Human(NamedTuple):
+
+    name: str
+
+
+async def is_type_of_error(*_args):
+    raise RuntimeError("We are testing this error")
+
+
+def get_is_type_of(type_):
+    async def is_type_of(obj, _info):
+        return isinstance(obj, type_)
+
+    return is_type_of
+
+
+def get_type_resolver(types):
+    async def resolve(obj, _info, _type):
+        return resolve_thunk(types)[obj.__class__]
+
+    return resolve
+
+
+def resolve_thunk(thunk):
+    return thunk() if callable(thunk) else thunk
+
+
+def describe_execute_handles_asynchronous_execution_of_abstract_types():
+    @mark.asyncio
+    async def is_type_of_used_to_resolve_runtime_type_for_interface():
+        PetType = GraphQLInterfaceType("Pet", {"name": GraphQLField(GraphQLString)})
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+            is_type_of=get_is_type_of(Dog),
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+            is_type_of=get_is_type_of(Cat),
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_args: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                        ],
+                    )
+                },
+            ),
+            types=[CatType, DogType],
+        )
+
+        source = """
+            {
+              pets {
+                name
+                ... on Dog {
+                  woofs
+                }
+                ... on Cat {
+                  meows
+                }
+              }
+            }
+            """
+
+        result = await graphql(schema=schema, source=source)
+        assert result == (
+            {
+                "pets": [
+                    {"name": "Odie", "woofs": True},
+                    {"name": "Garfield", "meows": False},
+                ]
+            },
+            None,
+        )
+
+    @mark.asyncio
+    async def is_type_of_with_async_error():
+        PetType = GraphQLInterfaceType("Pet", {"name": GraphQLField(GraphQLString)})
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+            is_type_of=is_type_of_error,
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+            is_type_of=get_is_type_of(Cat),
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_args: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                        ],
+                    )
+                },
+            ),
+            types=[CatType, DogType],
+        )
+
+        source = """
+            {
+              pets {
+                name
+                ... on Dog {
+                  woofs
+                }
+                ... on Cat {
+                  meows
+                }
+              }
+            }
+            """
+
+        result = await graphql(schema=schema, source=source)
+        # Note: we get two errors, because first all types are resolved
+        # and only then they are checked sequentially
+        assert result == (
+            {"pets": [None, None]},
+            [
+                {
+                    "message": "We are testing this error",
+                    "locations": [(3, 15)],
+                    "path": ["pets", 0],
+                },
+                {
+                    "message": "We are testing this error",
+                    "locations": [(3, 15)],
+                    "path": ["pets", 1],
+                },
+            ],
+        )
+
+    @mark.asyncio
+    async def is_type_of_with_no_suitable_type():
+        PetType = GraphQLInterfaceType("Pet", {"name": GraphQLField(GraphQLString)})
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+            is_type_of=get_is_type_of(Cat),
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_args: [Dog("Odie", True)],
+                    )
+                },
+            ),
+            types=[DogType],
+        )
+
+        source = """
+            {
+              pets {
+                name
+                ... on Dog {
+                  woofs
+                }
+              }
+            }
+            """
+
+        result = await graphql(schema=schema, source=source)
+        msg = (
+            "Abstract type 'Pet' must resolve to an Object type at runtime"
+            " for field 'Query.pets' with value ('Odie', True), received 'None'."
+            " Either the 'Pet' type should provide a 'resolve_type' function"
+            " or each possible type should provide an 'is_type_of' function."
+        )
+        assert result == (
+            {"pets": [None]},
+            [{"message": msg, "locations": [(3, 15)], "path": ["pets", 0]}],
+        )
+
+    @mark.asyncio
+    async def is_type_of_used_to_resolve_runtime_type_for_union():
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            is_type_of=get_is_type_of(Dog),
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            is_type_of=get_is_type_of(Cat),
+        )
+
+        PetType = GraphQLUnionType("Pet", [CatType, DogType])
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_args: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                        ],
+                    )
+                },
+            )
+        )
+
+        source = """
+            {
+              pets {
+                ... on Dog {
+                  name
+                  woofs
+                }
+                ... on Cat {
+                  name
+                  meows
+                }
+              }
+            }
+            """
+
+        result = await graphql(schema=schema, source=source)
+        assert result == (
+            {
+                "pets": [
+                    {"name": "Odie", "woofs": True},
+                    {"name": "Garfield", "meows": False},
+                ]
+            },
+            None,
+        )
+
+    @mark.asyncio
+    async def resolve_type_on_interface_yields_useful_error():
+        CatType: GraphQLObjectType
+        DogType: GraphQLObjectType
+        HumanType: GraphQLObjectType
+
+        PetType = GraphQLInterfaceType(
+            "Pet",
+            {"name": GraphQLField(GraphQLString)},
+            resolve_type=get_type_resolver(
+                lambda: {Dog: DogType, Cat: CatType, Human: HumanType}
+            ),
+        )
+
+        HumanType = GraphQLObjectType("Human", {"name": GraphQLField(GraphQLString)})
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_args: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                            Human("Jon"),
+                        ],
+                    )
+                },
+            ),
+            types=[CatType, DogType],
+        )
+
+        source = """
+            {
+              pets {
+                name
+                ... on Dog {
+                  woofs
+                }
+                ... on Cat {
+                  meows
+                }
+              }
+            }
+            """
+
+        result = await graphql(schema=schema, source=source)
+        assert result == (
+            {
+                "pets": [
+                    {"name": "Odie", "woofs": True},
+                    {"name": "Garfield", "meows": False},
+                    None,
+                ]
+            },
+            [
+                {
+                    "message": "Runtime Object type 'Human'"
+                    " is not a possible type for 'Pet'.",
+                    "locations": [(3, 15)],
+                    "path": ["pets", 2],
+                }
+            ],
+        )
+
+    @mark.asyncio
+    async def resolve_type_on_union_yields_useful_error():
+        HumanType = GraphQLObjectType("Human", {"name": GraphQLField(GraphQLString)})
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+        )
+
+        PetType = GraphQLUnionType(
+            "Pet",
+            [DogType, CatType],
+            resolve_type=get_type_resolver(
+                {Dog: DogType, Cat: CatType, Human: HumanType}
+            ),
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_: [
+                            Dog("Odie", True),
+                            Cat("Garfield", False),
+                            Human("Jon"),
+                        ],
+                    )
+                },
+            )
+        )
+
+        source = """
+            {
+              pets {
+                ... on Dog {
+                  name
+                  woofs
+                }
+                ... on Cat {
+                  name
+                  meows
+                }
+              }
+            }
+            """
+
+        result = await graphql(schema=schema, source=source)
+        assert result == (
+            {
+                "pets": [
+                    {"name": "Odie", "woofs": True},
+                    {"name": "Garfield", "meows": False},
+                    None,
+                ]
+            },
+            [
+                {
+                    "message": "Runtime Object type 'Human'"
+                    " is not a possible type for 'Pet'.",
+                    "locations": [(3, 15)],
+                    "path": ["pets", 2],
+                }
+            ],
+        )
+
+    @mark.asyncio
+    async def resolve_type_allows_resolving_with_type_name():
+        PetType = GraphQLInterfaceType(
+            "Pet",
+            {"name": GraphQLField(GraphQLString)},
+            resolve_type=get_type_resolver({Dog: "Dog", Cat: "Cat"}),
+        )
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_: [Dog("Odie", True), Cat("Garfield", False)],
+                    )
+                },
+            ),
+            types=[CatType, DogType],
+        )
+
+        source = """{
+          pets {
+            name
+            ... on Dog {
+              woofs
+            }
+            ... on Cat {
+              meows
+            }
+          }
+        }"""
+
+        result = await graphql(schema=schema, source=source)
+        assert result == (
+            {
+                "pets": [
+                    {"name": "Odie", "woofs": True},
+                    {"name": "Garfield", "meows": False},
+                ]
+            },
+            None,
+        )
+
+    @mark.asyncio
+    async def resolve_type_can_be_caught():
+        PetType = GraphQLInterfaceType(
+            "Pet", {"name": GraphQLField(GraphQLString)}, resolve_type=is_type_of_error
+        )
+
+        DogType = GraphQLObjectType(
+            "Dog",
+            {
+                "name": GraphQLField(GraphQLString),
+                "woofs": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        CatType = GraphQLObjectType(
+            "Cat",
+            {
+                "name": GraphQLField(GraphQLString),
+                "meows": GraphQLField(GraphQLBoolean),
+            },
+            interfaces=[PetType],
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "pets": GraphQLField(
+                        GraphQLList(PetType),
+                        resolve=lambda *_: [Dog("Odie", True), Cat("Garfield", False)],
+                    )
+                },
+            ),
+            types=[CatType, DogType],
+        )
+
+        source = """{
+          pets {
+            name
+            ... on Dog {
+              woofs
+            }
+            ... on Cat {
+              meows
+            }
+          }
+        }"""
+
+        result = await graphql(schema=schema, source=source)
+        assert result == (
+            {"pets": [None, None]},
+            [
+                {
+                    "message": "We are testing this error",
+                    "locations": [(2, 11)],
+                    "path": ["pets", 0],
+                },
+                {
+                    "message": "We are testing this error",
+                    "locations": [(2, 11)],
+                    "path": ["pets", 1],
+                },
+            ],
+        )
diff --git a/tests/execution/test_customize.py b/tests/execution/test_customize.py
new file mode 100644
index 0000000..2f6d2c9
--- /dev/null
+++ b/tests/execution/test_customize.py
@@ -0,0 +1,41 @@
+from graphql.execution import execute, ExecutionContext
+from graphql.language import parse
+from graphql.type import GraphQLSchema, GraphQLObjectType, GraphQLString, GraphQLField
+
+
+def describe_customize_execution():
+    def uses_a_custom_field_resolver():
+        query = parse("{ foo }")
+
+        schema = GraphQLSchema(
+            GraphQLObjectType("Query", {"foo": GraphQLField(GraphQLString)})
+        )
+
+        # For the purposes of test, just return the name of the field!
+        def custom_resolver(_source, info, **_args):
+            return info.field_name
+
+        assert execute(schema, query, field_resolver=custom_resolver) == (
+            {"foo": "foo"},
+            None,
+        )
+
+    def uses_a_custom_execution_context_class():
+        query = parse("{ foo }")
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {"foo": GraphQLField(GraphQLString, resolve=lambda *_args: "bar")},
+            )
+        )
+
+        class TestExecutionContext(ExecutionContext):
+            def resolve_field(self, parent_type, source, field_nodes, path):
+                result = super().resolve_field(parent_type, source, field_nodes, path)
+                return result * 2  # type: ignore
+
+        assert execute(schema, query, execution_context_class=TestExecutionContext) == (
+            {"foo": "barbar"},
+            None,
+        )
diff --git a/tests/execution/test_directives.py b/tests/execution/test_directives.py
new file mode 100644
index 0000000..261a0dd
--- /dev/null
+++ b/tests/execution/test_directives.py
@@ -0,0 +1,245 @@
+from graphql import GraphQLSchema
+from graphql.execution import execute
+from graphql.language import parse
+from graphql.type import GraphQLObjectType, GraphQLField, GraphQLString
+
+schema = GraphQLSchema(
+    GraphQLObjectType(
+        "TestType", {"a": GraphQLField(GraphQLString), "b": GraphQLField(GraphQLString)}
+    )
+)
+
+
+# noinspection PyMethodMayBeStatic
+class RootValue:
+    def a(self, *_args):
+        return "a"
+
+    def b(self, *_args):
+        return "b"
+
+
+def execute_test_query(query):
+    document = parse(query)
+    return execute(schema, document, RootValue())
+
+
+def describe_execute_handles_directives():
+    def describe_works_without_directives():
+        def basic_query_works():
+            result = execute_test_query("{ a, b }")
+            assert result == ({"a": "a", "b": "b"}, None)
+
+    def describe_works_on_scalars():
+        def if_true_includes_scalar():
+            result = execute_test_query("{ a, b @include(if: true) }")
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def if_false_omits_on_scalar():
+            result = execute_test_query("{ a, b @include(if: false) }")
+            assert result == ({"a": "a"}, None)
+
+        def unless_false_includes_scalar():
+            result = execute_test_query("{ a, b @skip(if: false) }")
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def unless_true_omits_scalar():
+            result = execute_test_query("{ a, b @skip(if: true) }")
+            assert result == ({"a": "a"}, None)
+
+    def describe_works_on_fragment_spreads():
+        def if_false_omits_fragment_spread():
+            result = execute_test_query(
+                """
+                query Q {
+                  a
+                  ...Frag @include(if: false)
+                }
+                fragment Frag on TestType {
+                  b
+                }
+                """
+            )
+            assert result == ({"a": "a"}, None)
+
+        def if_true_includes_fragment_spread():
+            result = execute_test_query(
+                """
+                query Q {
+                  a
+                  ...Frag @include(if: true)
+                }
+                fragment Frag on TestType {
+                  b
+                }
+                """
+            )
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def unless_false_includes_fragment_spread():
+            result = execute_test_query(
+                """
+                query Q {
+                  a
+                  ...Frag @skip(if: false)
+                }
+                fragment Frag on TestType {
+                  b
+                }
+                """
+            )
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def unless_true_omits_fragment_spread():
+            result = execute_test_query(
+                """
+                query Q {
+                  a
+                  ...Frag @skip(if: true)
+                }
+                fragment Frag on TestType {
+                  b
+                }
+                """
+            )
+            assert result == ({"a": "a"}, None)
+
+    def describe_works_on_inline_fragment():
+        def if_false_omits_inline_fragment():
+            result = execute_test_query(
+                """
+                query Q {
+                  a
+                  ... on TestType @include(if: false) {
+                    b
+                  }
+                }
+                """
+            )
+            assert result == ({"a": "a"}, None)
+
+        def if_true_includes_inline_fragment():
+            result = execute_test_query(
+                """
+                query Q {
+                  a
+                  ... on TestType @include(if: true) {
+                    b
+                  }
+                }
+                """
+            )
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def unless_false_includes_inline_fragment():
+            result = execute_test_query(
+                """
+                query Q {
+                  a
+                  ... on TestType @skip(if: false) {
+                    b
+                  }
+                }
+                """
+            )
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def unless_true_omits_inline_fragment():
+            result = execute_test_query(
+                """
+                query Q {
+                  a
+                  ... on TestType @skip(if: true) {
+                    b
+                  }
+                }
+                """
+            )
+            assert result == ({"a": "a"}, None)
+
+    def describe_works_on_anonymous_inline_fragment():
+        def if_false_omits_anonymous_inline_fragment():
+            result = execute_test_query(
+                """
+                query {
+                  a
+                  ... @include(if: false) {
+                    b
+                  }
+                }
+                """
+            )
+            assert result == ({"a": "a"}, None)
+
+        def if_true_includes_anonymous_inline_fragment():
+            result = execute_test_query(
+                """
+                query {
+                  a
+                  ... @include(if: true) {
+                    b
+                  }
+                }
+                """
+            )
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def unless_false_includes_anonymous_inline_fragment():
+            result = execute_test_query(
+                """
+                query {
+                  a
+                  ... @skip(if: false) {
+                    b
+                  }
+                }
+                """
+            )
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def unless_true_omits_anonymous_inline_fragment():
+            result = execute_test_query(
+                """
+                query {
+                  a
+                  ... @skip(if: true) {
+                    b
+                  }
+                }
+                """
+            )
+            assert result == ({"a": "a"}, None)
+
+    def describe_works_with_skip_and_include_directives():
+        def include_and_no_skip():
+            result = execute_test_query(
+                """
+                {
+                  a
+                  b @include(if: true) @skip(if: false)
+                }
+                """
+            )
+            assert result == ({"a": "a", "b": "b"}, None)
+
+        def include_and_skip():
+            result = execute_test_query(
+                """
+                {
+                  a
+                  b @include(if: true) @skip(if: true)
+                }
+                """
+            )
+            assert result == ({"a": "a"}, None)
+
+        def no_include_or_skip():
+            result = execute_test_query(
+                """
+                {
+                  a
+                  b @include(if: false) @skip(if: false)
+                }
+                """
+            )
+            assert result == ({"a": "a"}, None)
diff --git a/tests/execution/test_executor.py b/tests/execution/test_executor.py
new file mode 100644
index 0000000..22a6587
--- /dev/null
+++ b/tests/execution/test_executor.py
@@ -0,0 +1,994 @@
+import asyncio
+from typing import cast, Awaitable
+
+from pytest import raises, mark  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.execution import execute
+from graphql.language import parse, FieldNode, OperationDefinitionNode
+from graphql.pyutils import inspect, is_awaitable, Undefined
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLField,
+    GraphQLInt,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLResolveInfo,
+    GraphQLSchema,
+    GraphQLScalarType,
+    GraphQLString,
+    ResponsePath,
+)
+
+
+def describe_execute_handles_basic_execution_tasks():
+    # noinspection PyTypeChecker
+    def throws_if_no_document_is_provided():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        with raises(TypeError) as exc_info:
+            assert execute(schema=schema, document=None)  # type: ignore
+
+        assert str(exc_info.value) == "Must provide document."
+
+    # noinspection PyTypeChecker
+    def throws_if_no_schema_is_provided():
+        document = parse("{ field }")
+
+        with raises(TypeError) as exc_info:
+            assert execute(schema=None, document=document)  # type: ignore
+
+        assert str(exc_info.value) == "Expected None to be a GraphQL schema."
+
+    def throws_on_invalid_variables():
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Type",
+                {
+                    "fieldA": GraphQLField(
+                        GraphQLString, args={"argA": GraphQLArgument(GraphQLInt)}
+                    )
+                },
+            )
+        )
+        document = parse(
+            """
+            query ($a: Int) {
+              fieldA(argA: $a)
+            }
+            """
+        )
+        variable_values = "{'a': 1}"
+
+        with raises(TypeError) as exc_info:
+            assert execute(
+                schema=schema,
+                document=document,
+                variable_values=variable_values,  # type: ignore
+            )
+
+        assert str(exc_info.value) == (
+            "Variable values must be provided as a dictionary"
+            " with variable names as keys. Perhaps look to see"
+            " if an unparsed JSON string was provided."
+        )
+
+    def accepts_positional_arguments():
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Type",
+                {"a": GraphQLField(GraphQLString, resolve=lambda obj, *args: obj)},
+            )
+        )
+
+        result = execute(schema, parse("{ a }"), "rootValue")
+
+        assert result == ({"a": "rootValue"}, None)
+
+    @mark.asyncio
+    async def executes_arbitrary_code():
+        # noinspection PyMethodMayBeStatic,PyMethodMayBeStatic
+        class Data:
+            def a(self, _info):
+                return "Apple"
+
+            def b(self, _info):
+                return "Banana"
+
+            def c(self, _info):
+                return "Cookie"
+
+            def d(self, _info):
+                return "Donut"
+
+            def e(self, _info):
+                return "Egg"
+
+            f = "Fish"
+
+            # Called only by DataType::pic static resolver
+            def pic(self, _info, size):
+                return f"Pic of size: {size}"
+
+            def deep(self, _info):
+                return DeepData()
+
+            def promise(self, _info):
+                return promise_data()
+
+        # noinspection PyMethodMayBeStatic,PyMethodMayBeStatic
+        class DeepData:
+            def a(self, _info):
+                return "Already Been Done"
+
+            def b(self, _info):
+                return "Boring"
+
+            def c(self, _info):
+                return ["Contrived", None, "Confusing"]
+
+            def deeper(self, _info):
+                return [Data(), None, Data()]
+
+        async def promise_data():
+            await asyncio.sleep(0)
+            return Data()
+
+        DeepDataType: GraphQLObjectType
+
+        DataType = GraphQLObjectType(
+            "DataType",
+            lambda: {
+                "a": GraphQLField(GraphQLString),
+                "b": GraphQLField(GraphQLString),
+                "c": GraphQLField(GraphQLString),
+                "d": GraphQLField(GraphQLString),
+                "e": GraphQLField(GraphQLString),
+                "f": GraphQLField(GraphQLString),
+                "pic": GraphQLField(
+                    GraphQLString,
+                    args={"size": GraphQLArgument(GraphQLInt)},
+                    resolve=lambda obj, info, size: obj.pic(info, size),
+                ),
+                "deep": GraphQLField(DeepDataType),
+                "promise": GraphQLField(DataType),
+            },
+        )
+
+        DeepDataType = GraphQLObjectType(
+            "DeepDataType",
+            {
+                "a": GraphQLField(GraphQLString),
+                "b": GraphQLField(GraphQLString),
+                "c": GraphQLField(GraphQLList(GraphQLString)),
+                "deeper": GraphQLField(GraphQLList(DataType)),
+            },
+        )
+
+        document = parse(
+            """
+            query ($size: Int) {
+              a,
+              b,
+              x: c
+              ...c
+              f
+              ...on DataType {
+                pic(size: $size)
+                promise {
+                  a
+                }
+              }
+              deep {
+                a
+                b
+                c
+                deeper {
+                  a
+                  b
+                }
+              }
+            }
+
+            fragment c on DataType {
+              d
+              e
+            }
+            """
+        )
+
+        awaitable_result = execute(
+            GraphQLSchema(DataType), document, Data(), variable_values={"size": 100}
+        )
+        assert isinstance(awaitable_result, Awaitable)
+        result = await awaitable_result
+
+        assert result == (
+            {
+                "a": "Apple",
+                "b": "Banana",
+                "x": "Cookie",
+                "d": "Donut",
+                "e": "Egg",
+                "f": "Fish",
+                "pic": "Pic of size: 100",
+                "promise": {"a": "Apple"},
+                "deep": {
+                    "a": "Already Been Done",
+                    "b": "Boring",
+                    "c": ["Contrived", None, "Confusing"],
+                    "deeper": [
+                        {"a": "Apple", "b": "Banana"},
+                        None,
+                        {"a": "Apple", "b": "Banana"},
+                    ],
+                },
+            },
+            None,
+        )
+
+    def merges_parallel_fragments():
+        Type = GraphQLObjectType(
+            "Type",
+            lambda: {
+                "a": GraphQLField(GraphQLString, resolve=lambda *_args: "Apple"),
+                "b": GraphQLField(GraphQLString, resolve=lambda *_args: "Banana"),
+                "c": GraphQLField(GraphQLString, resolve=lambda *_args: "Cherry"),
+                "deep": GraphQLField(Type, resolve=lambda *_args: {}),
+            },
+        )
+        schema = GraphQLSchema(Type)
+
+        ast = parse(
+            """
+            { a, ...FragOne, ...FragTwo }
+
+            fragment FragOne on Type {
+              b
+              deep { b, deeper: deep { b } }
+            }
+
+            fragment FragTwo on Type {
+              c
+              deep { c, deeper: deep { c } }
+            }
+            """
+        )
+
+        result = execute(schema, ast)
+        assert result == (
+            {
+                "a": "Apple",
+                "b": "Banana",
+                "c": "Cherry",
+                "deep": {
+                    "b": "Banana",
+                    "c": "Cherry",
+                    "deeper": {"b": "Banana", "c": "Cherry"},
+                },
+            },
+            None,
+        )
+
+    def provides_info_about_current_execution_state():
+        resolved_infos = []
+
+        def resolve(_obj, info):
+            resolved_infos.append(info)
+
+        test_type = GraphQLObjectType(
+            "Test", {"test": GraphQLField(GraphQLString, resolve=resolve)}
+        )
+
+        schema = GraphQLSchema(test_type)
+
+        document = parse("query ($var: String) { result: test }")
+        root_value = {"root": "val"}
+        variable_values = {"var": "abc"}
+        execute(schema, document, root_value, variable_values=variable_values)
+
+        assert len(resolved_infos) == 1
+        operation = cast(OperationDefinitionNode, document.definitions[0])
+        assert operation and operation.kind == "operation_definition"
+        field = cast(FieldNode, operation.selection_set.selections[0])
+        assert resolved_infos[0] == GraphQLResolveInfo(
+            field_name="test",
+            field_nodes=[field],
+            return_type=GraphQLString,
+            parent_type=cast(GraphQLObjectType, schema.query_type),
+            path=ResponsePath(None, "result"),
+            schema=schema,
+            fragments={},
+            root_value=root_value,
+            operation=operation,
+            variable_values=variable_values,
+            context=None,
+            is_awaitable=is_awaitable,
+        )
+
+    def threads_root_value_context_correctly():
+        resolved_values = []
+
+        class Data:
+            context_thing = "thing"
+
+        def resolve(obj, _info):
+            resolved_values.append(obj)
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Type", {"a": GraphQLField(GraphQLString, resolve=resolve)}
+            )
+        )
+
+        document = parse("query Example { a }")
+        root_value = Data()
+        execute(schema, document, root_value)
+
+        assert len(resolved_values) == 1
+        assert resolved_values[0] is root_value
+
+    def correctly_threads_arguments():
+        resolved_args = []
+
+        def resolve(_obj, _info, **args):
+            resolved_args.append(args)
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Type",
+                {
+                    "b": GraphQLField(
+                        GraphQLString,
+                        args={
+                            "numArg": GraphQLArgument(GraphQLInt),
+                            "stringArg": GraphQLArgument(GraphQLString),
+                        },
+                        resolve=resolve,
+                    )
+                },
+            )
+        )
+
+        document = parse(
+            """
+            query Example {
+              b(numArg: 123, stringArg: "foo")
+            }
+            """
+        )
+
+        execute(schema, document)
+
+        assert len(resolved_args) == 1
+        assert resolved_args[0] == {"numArg": 123, "stringArg": "foo"}
+
+    @mark.asyncio
+    async def nulls_out_error_subtrees():
+        document = parse(
+            """
+            {
+              syncOk
+              syncError
+              syncRawError
+              syncReturnError
+              syncReturnErrorList
+              asyncOk
+              asyncError
+              asyncRawError
+              asyncReturnError
+              asyncReturnErrorWithExtensions
+            }
+            """
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Type",
+                {
+                    "syncOk": GraphQLField(GraphQLString),
+                    "syncError": GraphQLField(GraphQLString),
+                    "syncRawError": GraphQLField(GraphQLString),
+                    "syncReturnError": GraphQLField(GraphQLString),
+                    "syncReturnErrorList": GraphQLField(GraphQLList(GraphQLString)),
+                    "asyncOk": GraphQLField(GraphQLString),
+                    "asyncError": GraphQLField(GraphQLString),
+                    "asyncErrorWithExtensions": GraphQLField(GraphQLString),
+                    "asyncRawError": GraphQLField(GraphQLString),
+                    "asyncReturnError": GraphQLField(GraphQLString),
+                    "asyncReturnErrorWithExtensions": GraphQLField(GraphQLString),
+                },
+            )
+        )
+
+        # noinspection PyPep8Naming,PyMethodMayBeStatic
+        class Data:
+            def syncOk(self, _info):
+                return "sync ok"
+
+            def syncError(self, _info):
+                raise GraphQLError("Error getting syncError")
+
+            def syncRawError(self, _info):
+                raise Exception("Error getting syncRawError")
+
+            def syncReturnError(self, _info):
+                return Exception("Error getting syncReturnError")
+
+            def syncReturnErrorList(self, _info):
+                return [
+                    "sync0",
+                    Exception("Error getting syncReturnErrorList1"),
+                    "sync2",
+                    Exception("Error getting syncReturnErrorList3"),
+                ]
+
+            async def asyncOk(self, _info):
+                return "async ok"
+
+            async def asyncError(self, _info):
+                raise GraphQLError("Error getting asyncError")
+
+            async def asyncRawError(self, _info):
+                raise Exception("Error getting asyncRawError")
+
+            async def asyncReturnError(self, _info):
+                return GraphQLError("Error getting asyncReturnError")
+
+            async def asyncReturnErrorWithExtensions(self, _info):
+                return GraphQLError(
+                    "Error getting asyncReturnErrorWithExtensions",
+                    extensions={"foo": "bar"},
+                )
+
+        awaitable_result = execute(schema, document, Data())
+        assert isinstance(awaitable_result, Awaitable)
+        result = await awaitable_result
+
+        assert result == (
+            {
+                "syncOk": "sync ok",
+                "syncError": None,
+                "syncRawError": None,
+                "syncReturnError": None,
+                "syncReturnErrorList": ["sync0", None, "sync2", None],
+                "asyncOk": "async ok",
+                "asyncError": None,
+                "asyncRawError": None,
+                "asyncReturnError": None,
+                "asyncReturnErrorWithExtensions": None,
+            },
+            [
+                {
+                    "message": "Error getting syncError",
+                    "locations": [(4, 15)],
+                    "path": ["syncError"],
+                },
+                {
+                    "message": "Error getting syncRawError",
+                    "locations": [(5, 15)],
+                    "path": ["syncRawError"],
+                },
+                {
+                    "message": "Error getting syncReturnError",
+                    "locations": [(6, 15)],
+                    "path": ["syncReturnError"],
+                },
+                {
+                    "message": "Error getting syncReturnErrorList1",
+                    "locations": [(7, 15)],
+                    "path": ["syncReturnErrorList", 1],
+                },
+                {
+                    "message": "Error getting syncReturnErrorList3",
+                    "locations": [(7, 15)],
+                    "path": ["syncReturnErrorList", 3],
+                },
+                {
+                    "message": "Error getting asyncError",
+                    "locations": [(9, 15)],
+                    "path": ["asyncError"],
+                },
+                {
+                    "message": "Error getting asyncRawError",
+                    "locations": [(10, 15)],
+                    "path": ["asyncRawError"],
+                },
+                {
+                    "message": "Error getting asyncReturnError",
+                    "locations": [(11, 15)],
+                    "path": ["asyncReturnError"],
+                },
+                {
+                    "message": "Error getting asyncReturnErrorWithExtensions",
+                    "locations": [(12, 15)],
+                    "path": ["asyncReturnErrorWithExtensions"],
+                    "extensions": {"foo": "bar"},
+                },
+            ],
+        )
+
+    def full_response_path_is_included_for_non_nullable_fields():
+        def resolve_ok(*_args):
+            return {}
+
+        def resolve_error(*_args):
+            raise Exception("Catch me if you can")
+
+        A = GraphQLObjectType(
+            "A",
+            lambda: {
+                "nullableA": GraphQLField(A, resolve=resolve_ok),
+                "nonNullA": GraphQLField(GraphQLNonNull(A), resolve=resolve_ok),
+                "throws": GraphQLField(GraphQLNonNull(A), resolve=resolve_error),
+            },
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "query", lambda: {"nullableA": GraphQLField(A, resolve=resolve_ok)}
+            )
+        )
+
+        document = parse(
+            """
+            query {
+              nullableA {
+                aliasedA: nullableA {
+                  nonNullA {
+                    anotherA: nonNullA {
+                      throws
+                    }
+                  }
+                }
+              }
+            }
+            """
+        )
+
+        assert execute(schema, document) == (
+            {"nullableA": {"aliasedA": None}},
+            [
+                {
+                    "message": "Catch me if you can",
+                    "locations": [(7, 23)],
+                    "path": ["nullableA", "aliasedA", "nonNullA", "anotherA", "throws"],
+                }
+            ],
+        )
+
+    def uses_the_inline_operation_if_no_operation_name_is_provided():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse("{ a }")
+
+        class Data:
+            a = "b"
+
+        result = execute(schema, document, Data())
+        assert result == ({"a": "b"}, None)
+
+    def uses_the_only_operation_if_no_operation_name_is_provided():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse("query Example { a }")
+
+        class Data:
+            a = "b"
+
+        result = execute(schema, document, Data())
+        assert result == ({"a": "b"}, None)
+
+    def uses_the_named_operation_if_operation_name_is_provided():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse(
+            """
+            query Example { first: a }
+            query OtherExample { second: a }
+            """
+        )
+
+        class Data:
+            a = "b"
+
+        result = execute(schema, document, Data(), operation_name="OtherExample")
+        assert result == ({"second": "b"}, None)
+
+    def provides_error_if_no_operation_is_provided():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse("fragment Example on Type { a }")
+
+        class Data:
+            a = "b"
+
+        result = execute(schema, document, Data())
+        assert result == (None, [{"message": "Must provide an operation."}])
+
+    def errors_if_no_operation_name_is_provided_with_multiple_operations():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse(
+            """
+            query Example { a }
+            query OtherExample { a }
+            """
+        )
+
+        result = execute(schema, document)
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Must provide operation name if query contains"
+                    " multiple operations."
+                }
+            ],
+        )
+
+    def errors_if_unknown_operation_name_is_provided():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse(
+            """
+            query Example { a }
+            query OtherExample { a }
+            """
+        )
+
+        result = execute(schema, document, operation_name="UnknownExample")
+        assert result == (
+            None,
+            [{"message": "Unknown operation named 'UnknownExample'."}],
+        )
+
+    def errors_if_empty_string_is_provided_as_operation_name():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse("{ a }")
+
+        result = execute(schema, document, operation_name="")
+        assert result == (None, [{"message": "Unknown operation named ''."}],)
+
+    def uses_the_query_schema_for_queries():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)}),
+            GraphQLObjectType("M", {"c": GraphQLField(GraphQLString)}),
+            GraphQLObjectType("S", {"a": GraphQLField(GraphQLString)}),
+        )
+
+        document = parse(
+            """
+            query Q { a }
+            mutation M { c }
+            subscription S { a }
+            """
+        )
+
+        class Data:
+            a = "b"
+            c = "d"
+
+        result = execute(schema, document, Data(), operation_name="Q")
+        assert result == ({"a": "b"}, None)
+
+    def uses_the_mutation_schema_for_mutations():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)}),
+            GraphQLObjectType("M", {"c": GraphQLField(GraphQLString)}),
+        )
+
+        document = parse(
+            """
+            query Q { a }
+            mutation M { c }
+            """
+        )
+
+        class Data:
+            a = "b"
+            c = "d"
+
+        result = execute(schema, document, Data(), operation_name="M")
+        assert result == ({"c": "d"}, None)
+
+    def uses_the_subscription_schema_for_subscriptions():
+        schema = GraphQLSchema(
+            query=GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)}),
+            subscription=GraphQLObjectType("S", {"a": GraphQLField(GraphQLString)}),
+        )
+
+        document = parse(
+            """
+            query Q { a }
+            subscription S { a }
+            """
+        )
+
+        class Data:
+            a = "b"
+            c = "d"
+
+        result = execute(schema, document, Data(), operation_name="S")
+        assert result == ({"a": "b"}, None)
+
+    @mark.asyncio
+    async def correct_field_ordering_despite_execution_order():
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Type",
+                {
+                    "a": GraphQLField(GraphQLString),
+                    "b": GraphQLField(GraphQLString),
+                    "c": GraphQLField(GraphQLString),
+                    "d": GraphQLField(GraphQLString),
+                    "e": GraphQLField(GraphQLString),
+                },
+            )
+        )
+
+        document = parse("{ a, b, c, d, e}")
+
+        # noinspection PyMethodMayBeStatic,PyMethodMayBeStatic
+        class Data:
+            def a(self, _info):
+                return "a"
+
+            async def b(self, _info):
+                return "b"
+
+            def c(self, _info):
+                return "c"
+
+            async def d(self, _info):
+                return "d"
+
+            def e(self, _info):
+                return "e"
+
+        awaitable_result = execute(schema, document, Data())
+        assert isinstance(awaitable_result, Awaitable)
+        result = await awaitable_result
+
+        assert result == ({"a": "a", "b": "b", "c": "c", "d": "d", "e": "e"}, None)
+
+    def avoids_recursion():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Type", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse(
+            """
+            query Q {
+              a
+              ...Frag
+              ...Frag
+            }
+
+            fragment Frag on Type {
+              a,
+              ...Frag
+            }
+            """
+        )
+
+        class Data:
+            a = "b"
+
+        result = execute(schema, document, Data(), operation_name="Q")
+
+        assert result == ({"a": "b"}, None)
+
+    def ignores_missing_sub_selections_on_fields():
+        some_type = GraphQLObjectType("SomeType", {"b": GraphQLField(GraphQLString)})
+        schema = GraphQLSchema(
+            GraphQLObjectType("Query", {"a": GraphQLField(some_type)})
+        )
+        document = parse("{ a }")
+        root_value = {"a": {"b": "c"}}
+
+        result = execute(schema, document, root_value)
+        assert result == ({"a": {}}, None)
+
+    def does_not_include_illegal_fields_in_output():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Q", {"a": GraphQLField(GraphQLString)})
+        )
+
+        document = parse("{ thisIsIllegalDoNotIncludeMe }")
+
+        result = execute(schema, document)
+
+        assert result == ({}, None)
+
+    def does_not_include_arguments_that_were_not_set():
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Type",
+                {
+                    "field": GraphQLField(
+                        GraphQLString,
+                        args={
+                            "a": GraphQLArgument(GraphQLBoolean),
+                            "b": GraphQLArgument(GraphQLBoolean),
+                            "c": GraphQLArgument(GraphQLBoolean),
+                            "d": GraphQLArgument(GraphQLInt),
+                            "e": GraphQLArgument(GraphQLInt),
+                        },
+                        resolve=lambda _source, _info, **args: inspect(args),
+                    )
+                },
+            )
+        )
+
+        document = parse("{ field(a: true, c: false, e: 0) }")
+
+        assert execute(schema, document) == (
+            {"field": "{'a': True, 'c': False, 'e': 0}"},
+            None,
+        )
+
+    @mark.asyncio
+    async def fails_when_is_type_of_check_is_not_met():
+        class Special:
+            value: str
+
+            def __init__(self, value):
+                self.value = value
+
+        class NotSpecial:
+            value: str
+
+            def __init__(self, value):
+                self.value = value
+
+        def is_type_of_special(obj, _info):
+            is_special = isinstance(obj, Special)
+            if not _info.context["async"]:
+                return is_special
+
+            async def async_is_special():
+                return is_special
+
+            return async_is_special()
+
+        SpecialType = GraphQLObjectType(
+            "SpecialType",
+            {"value": GraphQLField(GraphQLString)},
+            is_type_of=is_type_of_special,
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query", {"specials": GraphQLField(GraphQLList(SpecialType))}
+            )
+        )
+
+        document = parse("{ specials { value } }")
+        root_value = {"specials": [Special("foo"), NotSpecial("bar")]}
+
+        result = execute(schema, document, root_value, {"async": False})
+        assert not isinstance(result, Awaitable)
+        assert result == (
+            {"specials": [{"value": "foo"}, None]},
+            [
+                {
+                    "message": "Expected value of type 'SpecialType' but got:"
+                    " <NotSpecial instance>.",
+                    "locations": [(1, 3)],
+                    "path": ["specials", 1],
+                }
+            ],
+        )
+
+        async_result = execute(schema, document, root_value, {"async": True})
+        assert isinstance(async_result, Awaitable)
+        awaited_result = await async_result
+        assert awaited_result == result
+
+    def fails_when_serialize_of_custom_scalar_does_not_return_a_value():
+        custom_scalar = GraphQLScalarType(
+            "CustomScalar", serialize=lambda _value: Undefined  # returns nothing
+        )
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "customScalar": GraphQLField(
+                        custom_scalar, resolve=lambda *_args: "CUSTOM_VALUE"
+                    )
+                },
+            )
+        )
+
+        result = execute(schema, parse("{ customScalar }"))
+        assert result == (
+            {"customScalar": None},
+            [
+                {
+                    "message": "Expected a value of type 'CustomScalar'"
+                    " but received: 'CUSTOM_VALUE'",
+                    "locations": [(1, 3)],
+                    "path": ["customScalar"],
+                }
+            ],
+        )
+
+    def executes_ignoring_invalid_non_executable_definitions():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Query", {"foo": GraphQLField(GraphQLString)})
+        )
+
+        document = parse(
+            """
+            { foo }
+
+            type Query { bar: String }
+            """
+        )
+
+        result = execute(schema, document)
+        assert result == ({"foo": None}, None)
+
+    def uses_a_custom_field_resolver():
+        schema = GraphQLSchema(
+            GraphQLObjectType("Query", {"foo": GraphQLField(GraphQLString)})
+        )
+        document = parse("{ foo }")
+
+        def field_resolver(_source, info):
+            # For the purposes of test, just return the name of the field!
+            return info.field_name
+
+        result = execute(schema, document, field_resolver=field_resolver)
+        assert result == ({"foo": "foo"}, None)
+
+    def uses_a_custom_type_resolver():
+        document = parse("{ foo { bar } }")
+
+        foo_interface = GraphQLInterfaceType(
+            "FooInterface", {"bar": GraphQLField(GraphQLString)}
+        )
+
+        foo_object = GraphQLObjectType(
+            "FooObject", {"bar": GraphQLField(GraphQLString)}, [foo_interface]
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType("Query", {"foo": GraphQLField(foo_interface)}),
+            types=[foo_object],
+        )
+
+        possible_types = None
+
+        def type_resolver(_source, info, abstract_type):
+            # Resolver should be able to figure out all possible types on its own
+            nonlocal possible_types
+            possible_types = info.schema.get_possible_types(abstract_type)
+            return "FooObject"
+
+        root_value = {"foo": {"bar": "bar"}}
+        result = execute(schema, document, root_value, type_resolver=type_resolver)
+
+        assert result == ({"foo": {"bar": "bar"}}, None)
+        assert possible_types == [foo_object]
diff --git a/tests/execution/test_lists.py b/tests/execution/test_lists.py
new file mode 100644
index 0000000..06569fa
--- /dev/null
+++ b/tests/execution/test_lists.py
@@ -0,0 +1,557 @@
+from collections import namedtuple
+from gc import collect
+
+from pytest import mark  # type: ignore
+
+from graphql.language import parse
+from graphql.type import (
+    GraphQLField,
+    GraphQLInt,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+from graphql.execution import execute
+
+Data = namedtuple("Data", "test")
+
+
+async def get_async(value):
+    return value
+
+
+async def raise_async(msg):
+    raise RuntimeError(msg)
+
+
+def get_response(test_type, test_data):
+    data = Data(test=test_data)
+
+    data_type = GraphQLObjectType(
+        "DataType",
+        lambda: {
+            "test": GraphQLField(test_type),
+            "nest": GraphQLField(data_type, resolve=lambda *_args: data),
+        },
+    )
+
+    return execute(
+        schema=GraphQLSchema(data_type),
+        document=parse("{ nest { test } }"),
+        context_value=data,
+    )
+
+
+def check_response(response, expected):
+    if not response.errors:
+        response = response.data
+    assert response == expected
+
+
+def check(test_type, test_data, expected):
+
+    check_response(get_response(test_type, test_data), expected)
+
+
+async def check_async(test_type, test_data, expected):
+    check_response(await get_response(test_type, test_data), expected)
+
+    # Note: When Array values are rejected asynchronously,
+    # the remaining values may not be awaited any more.
+    # We manually run a garbage collection after each test so that
+    # these warnings appear immediately and can be filtered out.
+    collect()
+
+
+def describe_execute_accepts_any_iterable_as_list_value():
+    def accepts_a_set_as_a_list_value():
+        # We need to use a dict instead of a set,
+        # since sets are not ordered in Python.
+        check(
+            GraphQLList(GraphQLString),
+            dict.fromkeys(["apple", "banana", "coconut"]),
+            {"nest": {"test": ["apple", "banana", "coconut"]}},
+        )
+
+    def accepts_a_generator_as_a_list_value():
+        def yield_items():
+            yield "one"
+            yield 2
+            yield True
+
+        check(
+            GraphQLList(GraphQLString),
+            yield_items(),
+            {"nest": {"test": ["one", "2", "true"]}},
+        )
+
+    def accepts_function_arguments_as_a_list_value():
+        def get_args(*args):
+            return args  # actually just a tuple, nothing special in Python
+
+        check(
+            GraphQLList(GraphQLString),
+            get_args("one", "two"),
+            {"nest": {"test": ["one", "two"]}},
+        )
+
+    def does_not_accept_iterable_string_literal_as_a_list_value():
+        check(
+            GraphQLList(GraphQLString),
+            "Singular",
+            (
+                {"nest": {"test": None}},
+                [
+                    {
+                        "message": "Expected Iterable,"
+                        " but did not find one for field 'DataType.test'.",
+                        "locations": [(1, 10)],
+                        "path": ["nest", "test"],
+                    }
+                ],
+            ),
+        )
+
+
+def describe_execute_handles_list_nullability():
+    def describe_list():
+        type_ = GraphQLList(GraphQLInt)
+
+        def describe_sync_list():
+            def contains_values():
+                check(type_, [1, 2], {"nest": {"test": [1, 2]}})
+
+            def contains_null():
+                check(type_, [1, None, 2], {"nest": {"test": [1, None, 2]}})
+
+            def returns_null():
+                check(type_, None, {"nest": {"test": None}})
+
+        def describe_async_list():
+            @mark.asyncio
+            async def contains_values():
+                await check_async(type_, get_async([1, 2]), {"nest": {"test": [1, 2]}})
+
+            @mark.asyncio
+            async def contains_null():
+                await check_async(
+                    type_, get_async([1, None, 2]), {"nest": {"test": [1, None, 2]}}
+                )
+
+            @mark.asyncio
+            async def returns_null():
+                await check_async(type_, get_async(None), {"nest": {"test": None}})
+
+            @mark.asyncio
+            async def async_error():
+                await check_async(
+                    type_,
+                    raise_async("bad"),
+                    (
+                        {"nest": {"test": None}},
+                        [
+                            {
+                                "message": "bad",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test"],
+                            }
+                        ],
+                    ),
+                )
+
+        def describe_list_async():
+            @mark.asyncio
+            async def contains_values():
+                await check_async(
+                    type_, [get_async(1), get_async(2)], {"nest": {"test": [1, 2]}}
+                )
+
+            @mark.asyncio
+            async def contains_null():
+                await check_async(
+                    type_,
+                    [get_async(1), get_async(None), get_async(2)],
+                    {"nest": {"test": [1, None, 2]}},
+                )
+
+            @mark.asyncio
+            async def contains_async_error():
+                await check_async(
+                    type_,
+                    [get_async(1), raise_async("bad"), get_async(2)],
+                    (
+                        {"nest": {"test": [1, None, 2]}},
+                        [
+                            {
+                                "message": "bad",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+    def describe_not_null_list():
+        type_ = GraphQLNonNull(GraphQLList(GraphQLInt))
+
+        def describe_sync_list():
+            def contains_values():
+                check(type_, [1, 2], {"nest": {"test": [1, 2]}})
+
+            def contains_null():
+                check(type_, [1, None, 2], {"nest": {"test": [1, None, 2]}})
+
+            def returns_null():
+                check(
+                    type_,
+                    None,
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test"],
+                            }
+                        ],
+                    ),
+                )
+
+        def describe_async_list():
+            @mark.asyncio
+            async def contains_values():
+                await check_async(type_, get_async([1, 2]), {"nest": {"test": [1, 2]}})
+
+            @mark.asyncio
+            async def contains_null():
+                await check_async(
+                    type_, get_async([1, None, 2]), {"nest": {"test": [1, None, 2]}}
+                )
+
+            @mark.asyncio
+            async def returns_null():
+                await check_async(
+                    type_,
+                    get_async(None),
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test"],
+                            }
+                        ],
+                    ),
+                )
+
+            @mark.asyncio
+            async def async_error():
+                await check_async(
+                    type_,
+                    raise_async("bad"),
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "bad",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test"],
+                            }
+                        ],
+                    ),
+                )
+
+        def describe_list_async():
+            @mark.asyncio
+            async def contains_values():
+                await check_async(
+                    type_, [get_async(1), get_async(2)], {"nest": {"test": [1, 2]}}
+                )
+
+            @mark.asyncio
+            async def contains_null():
+                await check_async(
+                    type_,
+                    [get_async(1), get_async(None), get_async(2)],
+                    {"nest": {"test": [1, None, 2]}},
+                )
+
+            @mark.asyncio
+            async def contains_async_error():
+                await check_async(
+                    type_,
+                    [get_async(1), raise_async("bad"), get_async(2)],
+                    (
+                        {"nest": {"test": [1, None, 2]}},
+                        [
+                            {
+                                "message": "bad",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+    def describe_list_not_null():
+        type_ = GraphQLList(GraphQLNonNull(GraphQLInt))
+
+        def describe_sync_list():
+            def contains_values():
+                check(type_, [1, 2], {"nest": {"test": [1, 2]}})
+
+            def contains_null():
+                check(
+                    type_,
+                    [1, None, 2],
+                    (
+                        {"nest": {"test": None}},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+            def returns_null():
+                check(type_, None, {"nest": {"test": None}})
+
+        def describe_async_list():
+            @mark.asyncio
+            async def contains_values():
+                await check_async(type_, get_async([1, 2]), {"nest": {"test": [1, 2]}})
+
+            @mark.asyncio
+            async def contains_null():
+                await check_async(
+                    type_,
+                    get_async([1, None, 2]),
+                    (
+                        {"nest": {"test": None}},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+            @mark.asyncio
+            async def returns_null():
+                await check_async(type_, get_async(None), {"nest": {"test": None}})
+
+            @mark.asyncio
+            async def async_error():
+                await check_async(
+                    type_,
+                    raise_async("bad"),
+                    (
+                        {"nest": {"test": None}},
+                        [
+                            {
+                                "message": "bad",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test"],
+                            }
+                        ],
+                    ),
+                )
+
+        def describe_list_async():
+            @mark.asyncio
+            async def contains_values():
+                await check_async(
+                    type_, [get_async(1), get_async(2)], {"nest": {"test": [1, 2]}}
+                )
+
+            @mark.asyncio
+            @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+            async def contains_null():
+                await check_async(
+                    type_,
+                    [get_async(1), get_async(None), get_async(2)],
+                    (
+                        {"nest": {"test": None}},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+            @mark.asyncio
+            @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+            async def contains_async_error():
+                await check_async(
+                    type_,
+                    [get_async(1), raise_async("bad"), get_async(2)],
+                    (
+                        {"nest": {"test": None}},
+                        [
+                            {
+                                "message": "bad",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+    def describe_not_null_list_not_null():
+        type_ = GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLInt)))
+
+        def describe_sync_list():
+            def contains_values():
+                check(type_, [1, 2], {"nest": {"test": [1, 2]}})
+
+            def contains_null():
+                check(
+                    type_,
+                    [1, None, 2],
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+            def returns_null():
+                check(
+                    type_,
+                    None,
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test"],
+                            }
+                        ],
+                    ),
+                )
+
+        def describe_async_list():
+            @mark.asyncio
+            async def contains_values():
+                await check_async(type_, get_async([1, 2]), {"nest": {"test": [1, 2]}})
+
+            @mark.asyncio
+            async def contains_null():
+                await check_async(
+                    type_,
+                    get_async([1, None, 2]),
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+            @mark.asyncio
+            async def returns_null():
+                await check_async(
+                    type_,
+                    get_async(None),
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test"],
+                            }
+                        ],
+                    ),
+                )
+
+            @mark.asyncio
+            async def async_error():
+                await check_async(
+                    type_,
+                    raise_async("bad"),
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "bad",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test"],
+                            }
+                        ],
+                    ),
+                )
+
+        def describe_list_async():
+            @mark.asyncio
+            async def contains_values():
+                await check_async(
+                    type_, [get_async(1), get_async(2)], {"nest": {"test": [1, 2]}}
+                )
+
+            @mark.asyncio
+            @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+            async def contains_null():
+                await check_async(
+                    type_,
+                    [get_async(1), get_async(None), get_async(2)],
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "Cannot return null"
+                                " for non-nullable field DataType.test.",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
+
+            @mark.asyncio
+            @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+            async def contains_async_error():
+                await check_async(
+                    type_,
+                    [get_async(1), raise_async("bad"), get_async(2)],
+                    (
+                        {"nest": None},
+                        [
+                            {
+                                "message": "bad",
+                                "locations": [(1, 10)],
+                                "path": ["nest", "test", 1],
+                            }
+                        ],
+                    ),
+                )
diff --git a/tests/execution/test_middleware.py b/tests/execution/test_middleware.py
new file mode 100644
index 0000000..8d187bf
--- /dev/null
+++ b/tests/execution/test_middleware.py
@@ -0,0 +1,334 @@
+from typing import Awaitable
+
+from pytest import mark, raises  # type: ignore
+
+from graphql.execution import MiddlewareManager, execute
+from graphql.language.parser import parse
+from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
+
+
+def describe_middleware():
+    def describe_with_manager():
+        def default():
+            doc = parse("{ field }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                def field(self, _info):
+                    return "resolved"
+
+            test_type = GraphQLObjectType(
+                "TestType", {"field": GraphQLField(GraphQLString)}
+            )
+
+            middlewares = MiddlewareManager()
+            result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+
+            assert result.data["field"] == "resolved"  # type: ignore
+
+        def single_function():
+            doc = parse("{ first second }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                def first(self, _info):
+                    return "one"
+
+                def second(self, _info):
+                    return "two"
+
+            test_type = GraphQLObjectType(
+                "TestType",
+                {
+                    "first": GraphQLField(GraphQLString),
+                    "second": GraphQLField(GraphQLString),
+                },
+            )
+
+            def reverse_middleware(next_, *args, **kwargs):
+                return next_(*args, **kwargs)[::-1]
+
+            middlewares = MiddlewareManager(reverse_middleware)
+            result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+
+            assert result.data == {"first": "eno", "second": "owt"}  # type: ignore
+
+        def two_functions_and_field_resolvers():
+            doc = parse("{ first second }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                first = "one"
+                second = "two"
+
+            test_type = GraphQLObjectType(
+                "TestType",
+                {
+                    "first": GraphQLField(
+                        GraphQLString, resolve=lambda obj, _info: obj.first
+                    ),
+                    "second": GraphQLField(
+                        GraphQLString, resolve=lambda obj, _info: obj.second
+                    ),
+                },
+            )
+
+            def reverse_middleware(next_, *args, **kwargs):
+                return next_(*args, **kwargs)[::-1]
+
+            def capitalize_middleware(next_, *args, **kwargs):
+                return next_(*args, **kwargs).capitalize()
+
+            middlewares = MiddlewareManager(reverse_middleware, capitalize_middleware)
+            result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+
+            assert result.data == {"first": "Eno", "second": "Owt"}  # type: ignore
+
+        @mark.asyncio
+        async def single_async_function():
+            doc = parse("{ first second }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                async def first(self, _info):
+                    return "one"
+
+                async def second(self, _info):
+                    return "two"
+
+            test_type = GraphQLObjectType(
+                "TestType",
+                {
+                    "first": GraphQLField(GraphQLString),
+                    "second": GraphQLField(GraphQLString),
+                },
+            )
+
+            async def reverse_middleware(next_, *args, **kwargs):
+                return (await next_(*args, **kwargs))[::-1]
+
+            middlewares = MiddlewareManager(reverse_middleware)
+            awaitable_result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+            assert isinstance(awaitable_result, Awaitable)
+            result = await awaitable_result
+            assert result.data == {"first": "eno", "second": "owt"}
+
+        def single_object():
+            doc = parse("{ first second }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                def first(self, _info):
+                    return "one"
+
+                def second(self, _info):
+                    return "two"
+
+            test_type = GraphQLObjectType(
+                "TestType",
+                {
+                    "first": GraphQLField(GraphQLString),
+                    "second": GraphQLField(GraphQLString),
+                },
+            )
+
+            class ReverseMiddleware:
+
+                # noinspection PyMethodMayBeStatic
+                def resolve(self, next_, *args, **kwargs):
+                    return next_(*args, **kwargs)[::-1]
+
+            middlewares = MiddlewareManager(ReverseMiddleware())
+            result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+
+            assert result.data == {"first": "eno", "second": "owt"}  # type: ignore
+
+        def skip_middleware_without_resolve_method():
+            class BadMiddleware:
+                pass  # no resolve method here
+
+            assert execute(
+                GraphQLSchema(
+                    GraphQLObjectType("TestType", {"foo": GraphQLField(GraphQLString)},)
+                ),
+                parse("{ foo }"),
+                {"foo": "bar"},
+                middleware=MiddlewareManager(BadMiddleware()),
+            ) == ({"foo": "bar"}, None)
+
+        def with_function_and_object():
+            doc = parse("{ field }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                def field(self, _info):
+                    return "resolved"
+
+            test_type = GraphQLObjectType(
+                "TestType", {"field": GraphQLField(GraphQLString)}
+            )
+
+            def reverse_middleware(next_, *args, **kwargs):
+                return next_(*args, **kwargs)[::-1]
+
+            class CaptitalizeMiddleware:
+
+                # noinspection PyMethodMayBeStatic
+                def resolve(self, next_, *args, **kwargs):
+                    return next_(*args, **kwargs).capitalize()
+
+            middlewares = MiddlewareManager(reverse_middleware, CaptitalizeMiddleware())
+            result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+            assert result.data == {"field": "Devloser"}  # type: ignore
+
+            middlewares = MiddlewareManager(CaptitalizeMiddleware(), reverse_middleware)
+            result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+            assert result.data == {"field": "devloseR"}  # type: ignore
+
+        @mark.asyncio
+        async def with_async_function_and_object():
+            doc = parse("{ field }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                async def field(self, _info):
+                    return "resolved"
+
+            test_type = GraphQLObjectType(
+                "TestType", {"field": GraphQLField(GraphQLString)}
+            )
+
+            async def reverse_middleware(next_, *args, **kwargs):
+                return (await next_(*args, **kwargs))[::-1]
+
+            class CaptitalizeMiddleware:
+
+                # noinspection PyMethodMayBeStatic
+                async def resolve(self, next_, *args, **kwargs):
+                    return (await next_(*args, **kwargs)).capitalize()
+
+            middlewares = MiddlewareManager(reverse_middleware, CaptitalizeMiddleware())
+            awaitable_result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+            assert isinstance(awaitable_result, Awaitable)
+            result = await awaitable_result
+            assert result.data == {"field": "Devloser"}
+
+            middlewares = MiddlewareManager(CaptitalizeMiddleware(), reverse_middleware)
+            awaitable_result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+            assert isinstance(awaitable_result, Awaitable)
+            result = await awaitable_result
+            assert result.data == {"field": "devloseR"}
+
+    def describe_without_manager():
+        def no_middleware():
+            doc = parse("{ field }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                def field(self, _info):
+                    return "resolved"
+
+            test_type = GraphQLObjectType(
+                "TestType", {"field": GraphQLField(GraphQLString)}
+            )
+
+            result = execute(GraphQLSchema(test_type), doc, Data(), middleware=None)
+
+            assert result.data["field"] == "resolved"  # type: ignore
+
+        def empty_middleware_list():
+            doc = parse("{ field }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                def field(self, _info):
+                    return "resolved"
+
+            test_type = GraphQLObjectType(
+                "TestType", {"field": GraphQLField(GraphQLString)}
+            )
+
+            result = execute(GraphQLSchema(test_type), doc, Data(), middleware=[])
+
+            assert result.data["field"] == "resolved"  # type: ignore
+
+        def bad_middleware_object():
+            doc = parse("{ field }")
+
+            test_type = GraphQLObjectType(
+                "TestType", {"field": GraphQLField(GraphQLString)}
+            )
+
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                execute(
+                    GraphQLSchema(test_type),
+                    doc,
+                    None,
+                    middleware={"bad": "value"},  # type: ignore
+                )
+
+            assert str(exc_info.value) == (
+                "Middleware must be passed as a list or tuple of functions"
+                " or objects, or as a single MiddlewareManager object."
+                " Got {'bad': 'value'} instead."
+            )
+
+        def list_of_functions():
+            doc = parse("{ field }")
+
+            # noinspection PyMethodMayBeStatic
+            class Data:
+                def field(self, _info):
+                    return "resolved"
+
+            test_type = GraphQLObjectType(
+                "TestType", {"field": GraphQLField(GraphQLString)}
+            )
+
+            log = []
+
+            class LogMiddleware:
+                def __init__(self, name):
+                    self.name = name
+
+                # noinspection PyMethodMayBeStatic
+                def resolve(self, next_, *args, **kwargs):
+                    log.append(f"enter {self.name}")
+                    value = next_(*args, **kwargs)
+                    log.append(f"exit {self.name}")
+                    return value
+
+            middlewares = [LogMiddleware("A"), LogMiddleware("B"), LogMiddleware("C")]
+
+            result = execute(
+                GraphQLSchema(test_type), doc, Data(), middleware=middlewares
+            )
+            assert result.data == {"field": "resolved"}  # type: ignore
+
+            assert log == [
+                "enter C",
+                "enter B",
+                "enter A",
+                "exit A",
+                "exit B",
+                "exit C",
+            ]
diff --git a/tests/execution/test_mutations.py b/tests/execution/test_mutations.py
new file mode 100644
index 0000000..a256acc
--- /dev/null
+++ b/tests/execution/test_mutations.py
@@ -0,0 +1,197 @@
+import asyncio
+from typing import Awaitable
+
+from pytest import mark  # type: ignore
+
+from graphql.execution import execute
+from graphql.language import parse
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLField,
+    GraphQLInt,
+    GraphQLObjectType,
+    GraphQLSchema,
+)
+
+
+# noinspection PyPep8Naming
+class NumberHolder:
+
+    theNumber: int
+
+    def __init__(self, originalNumber: int):
+        self.theNumber = originalNumber
+
+
+# noinspection PyPep8Naming
+class Root:
+
+    numberHolder: NumberHolder
+
+    def __init__(self, originalNumber: int):
+        self.numberHolder = NumberHolder(originalNumber)
+
+    def immediately_change_the_number(self, newNumber: int) -> NumberHolder:
+        self.numberHolder.theNumber = newNumber
+        return self.numberHolder
+
+    async def promise_to_change_the_number(self, new_number: int) -> NumberHolder:
+        await asyncio.sleep(0)
+        return self.immediately_change_the_number(new_number)
+
+    def fail_to_change_the_number(self, newNumber: int):
+        raise RuntimeError(f"Cannot change the number to {newNumber}")
+
+    async def promise_and_fail_to_change_the_number(self, newNumber: int):
+        await asyncio.sleep(0)
+        self.fail_to_change_the_number(newNumber)
+
+
+numberHolderType = GraphQLObjectType(
+    "NumberHolder", {"theNumber": GraphQLField(GraphQLInt)}
+)
+
+# noinspection PyPep8Naming
+schema = GraphQLSchema(
+    GraphQLObjectType("Query", {"numberHolder": GraphQLField(numberHolderType)}),
+    GraphQLObjectType(
+        "Mutation",
+        {
+            "immediatelyChangeTheNumber": GraphQLField(
+                numberHolderType,
+                args={"newNumber": GraphQLArgument(GraphQLInt)},
+                resolve=lambda obj, _info, newNumber: obj.immediately_change_the_number(
+                    newNumber
+                ),
+            ),
+            "promiseToChangeTheNumber": GraphQLField(
+                numberHolderType,
+                args={"newNumber": GraphQLArgument(GraphQLInt)},
+                resolve=lambda obj, _info, newNumber: obj.promise_to_change_the_number(
+                    newNumber
+                ),
+            ),
+            "failToChangeTheNumber": GraphQLField(
+                numberHolderType,
+                args={"newNumber": GraphQLArgument(GraphQLInt)},
+                resolve=lambda obj, _info, newNumber: obj.fail_to_change_the_number(
+                    newNumber
+                ),
+            ),
+            "promiseAndFailToChangeTheNumber": GraphQLField(
+                numberHolderType,
+                args={"newNumber": GraphQLArgument(GraphQLInt)},
+                resolve=lambda obj, _info, newNumber: (
+                    obj.promise_and_fail_to_change_the_number(newNumber)
+                ),
+            ),
+        },
+    ),
+)
+
+
+def describe_execute_handles_mutation_execution_ordering():
+    @mark.asyncio
+    async def evaluates_mutations_serially():
+        document = parse(
+            """
+            mutation M {
+              first: immediatelyChangeTheNumber(newNumber: 1) {
+                theNumber
+              },
+              second: promiseToChangeTheNumber(newNumber: 2) {
+                theNumber
+              },
+              third: immediatelyChangeTheNumber(newNumber: 3) {
+                theNumber
+              }
+              fourth: promiseToChangeTheNumber(newNumber: 4) {
+                theNumber
+              },
+              fifth: immediatelyChangeTheNumber(newNumber: 5) {
+                theNumber
+              }
+            }
+            """
+        )
+
+        root_value = Root(6)
+        awaitable_result = execute(
+            schema=schema, document=document, root_value=root_value
+        )
+        assert isinstance(awaitable_result, Awaitable)
+        mutation_result = await awaitable_result
+
+        assert mutation_result == (
+            {
+                "first": {"theNumber": 1},
+                "second": {"theNumber": 2},
+                "third": {"theNumber": 3},
+                "fourth": {"theNumber": 4},
+                "fifth": {"theNumber": 5},
+            },
+            None,
+        )
+
+    def does_not_include_illegal_mutation_fields_in_output():
+        document = parse("mutation { thisIsIllegalDoNotIncludeMe }")
+
+        result = execute(schema=schema, document=document)
+        assert result == ({}, None)
+
+    @mark.asyncio
+    async def evaluates_mutations_correctly_in_presence_of_a_failed_mutation():
+        document = parse(
+            """
+            mutation M {
+              first: immediatelyChangeTheNumber(newNumber: 1) {
+                theNumber
+              },
+              second: promiseToChangeTheNumber(newNumber: 2) {
+                theNumber
+              },
+              third: failToChangeTheNumber(newNumber: 3) {
+                theNumber
+              }
+              fourth: promiseToChangeTheNumber(newNumber: 4) {
+                theNumber
+              },
+              fifth: immediatelyChangeTheNumber(newNumber: 5) {
+                theNumber
+              }
+              sixth: promiseAndFailToChangeTheNumber(newNumber: 6) {
+                theNumber
+              }
+            }
+            """
+        )
+
+        root_value = Root(6)
+        awaitable_result = execute(
+            schema=schema, document=document, root_value=root_value
+        )
+        assert isinstance(awaitable_result, Awaitable)
+        result = await awaitable_result
+
+        assert result == (
+            {
+                "first": {"theNumber": 1},
+                "second": {"theNumber": 2},
+                "third": None,
+                "fourth": {"theNumber": 4},
+                "fifth": {"theNumber": 5},
+                "sixth": None,
+            },
+            [
+                {
+                    "message": "Cannot change the number to 3",
+                    "locations": [(9, 15)],
+                    "path": ["third"],
+                },
+                {
+                    "message": "Cannot change the number to 6",
+                    "locations": [(18, 15)],
+                    "path": ["sixth"],
+                },
+            ],
+        )
diff --git a/tests/execution/test_nonnull.py b/tests/execution/test_nonnull.py
new file mode 100644
index 0000000..ab4a5ce
--- /dev/null
+++ b/tests/execution/test_nonnull.py
@@ -0,0 +1,696 @@
+import re
+from inspect import isawaitable
+
+from pytest import mark  # type: ignore
+
+from graphql.execution import execute
+from graphql.language import parse
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLField,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+from graphql.utilities import build_schema
+
+sync_error = RuntimeError("sync")
+sync_non_null_error = RuntimeError("syncNonNull")
+promise_error = RuntimeError("promise")
+promise_non_null_error = RuntimeError("promiseNonNull")
+
+
+# noinspection PyPep8Naming,PyMethodMayBeStatic
+class ThrowingData:
+    def sync(self, _info):
+        raise sync_error
+
+    def syncNonNull(self, _info):
+        raise sync_non_null_error
+
+    async def promise(self, _info):
+        raise promise_error
+
+    async def promiseNonNull(self, _info):
+        raise promise_non_null_error
+
+    def syncNest(self, _info):
+        return ThrowingData()
+
+    def syncNonNullNest(self, _info):
+        return ThrowingData()
+
+    async def promiseNest(self, _info):
+        return ThrowingData()
+
+    async def promiseNonNullNest(self, _info):
+        return ThrowingData()
+
+
+# noinspection PyPep8Naming,PyMethodMayBeStatic
+class NullingData:
+    def sync(self, _info):
+        return None
+
+    def syncNonNull(self, _info):
+        return None
+
+    async def promise(self, _info):
+        return None
+
+    async def promiseNonNull(self, _info):
+        return None
+
+    def syncNest(self, _info):
+        return NullingData()
+
+    def syncNonNullNest(self, _info):
+        return NullingData()
+
+    async def promiseNest(self, _info):
+        return NullingData()
+
+    async def promiseNonNullNest(self, _info):
+        return NullingData()
+
+
+schema = build_schema(
+    """
+    type DataType {
+      sync: String
+      syncNonNull: String!
+      promise: String
+      promiseNonNull: String!
+      syncNest: DataType
+      syncNonNullNest: DataType!
+      promiseNest: DataType
+      promiseNonNullNest: DataType!
+    }
+
+    schema {
+      query: DataType
+    }
+    """
+)
+
+
+def execute_query(query, root_value):
+    return execute(schema=schema, document=parse(query), root_value=root_value)
+
+
+# avoids also doing any nests
+def patch(data):
+    return re.sub(
+        r"\bsyncNonNull\b", "promiseNonNull", re.sub(r"\bsync\b", "promise", data)
+    )
+
+
+async def execute_sync_and_async(query, root_value):
+    sync_result = execute_query(query, root_value)
+    if isawaitable(sync_result):
+        sync_result = await sync_result
+    async_result = await execute_query(patch(query), root_value)
+
+    assert repr(async_result) == patch(repr(sync_result))
+    return sync_result
+
+
+def describe_execute_handles_non_nullable_types():
+    def describe_nulls_a_nullable_field():
+        query = """
+            {
+              sync
+            }
+            """
+
+        @mark.asyncio
+        async def returns_null():
+            result = await execute_sync_and_async(query, NullingData())
+            assert result == ({"sync": None}, None)
+
+        @mark.asyncio
+        async def throws():
+            result = await execute_sync_and_async(query, ThrowingData())
+            assert result == (
+                {"sync": None},
+                [
+                    {
+                        "message": str(sync_error),
+                        "path": ["sync"],
+                        "locations": [(3, 15)],
+                    }
+                ],
+            )
+
+    def describe_nulls_an_immediate_object_that_contains_a_non_null_field():
+
+        query = """
+            {
+              syncNest {
+                syncNonNull,
+              }
+            }
+            """
+
+        @mark.asyncio
+        async def returns_null():
+            result = await execute_sync_and_async(query, NullingData())
+            assert result == (
+                {"syncNest": None},
+                [
+                    {
+                        "message": "Cannot return null for non-nullable field"
+                        " DataType.syncNonNull.",
+                        "path": ["syncNest", "syncNonNull"],
+                        "locations": [(4, 17)],
+                    }
+                ],
+            )
+
+        @mark.asyncio
+        async def throws():
+            result = await execute_sync_and_async(query, ThrowingData())
+            assert result == (
+                {"syncNest": None},
+                [
+                    {
+                        "message": str(sync_non_null_error),
+                        "path": ["syncNest", "syncNonNull"],
+                        "locations": [(4, 17)],
+                    }
+                ],
+            )
+
+    def describe_nulls_a_promised_object_that_contains_a_non_null_field():
+        query = """
+            {
+              promiseNest {
+                syncNonNull,
+              }
+            }
+            """
+
+        @mark.asyncio
+        async def returns_null():
+            result = await execute_sync_and_async(query, NullingData())
+            assert result == (
+                {"promiseNest": None},
+                [
+                    {
+                        "message": "Cannot return null for non-nullable field"
+                        " DataType.syncNonNull.",
+                        "path": ["promiseNest", "syncNonNull"],
+                        "locations": [(4, 17)],
+                    }
+                ],
+            )
+
+        @mark.asyncio
+        async def throws():
+            result = await execute_sync_and_async(query, ThrowingData())
+            assert result == (
+                {"promiseNest": None},
+                [
+                    {
+                        "message": str(sync_non_null_error),
+                        "path": ["promiseNest", "syncNonNull"],
+                        "locations": [(4, 17)],
+                    }
+                ],
+            )
+
+    def describe_nulls_a_complex_tree_of_nullable_fields_each():
+        query = """
+            {
+              syncNest {
+                sync
+                promise
+                syncNest { sync promise }
+                promiseNest { sync promise }
+              }
+              promiseNest {
+                sync
+                promise
+                syncNest { sync promise }
+                promiseNest { sync promise }
+              }
+            }
+            """
+        data = {
+            "syncNest": {
+                "sync": None,
+                "promise": None,
+                "syncNest": {"sync": None, "promise": None},
+                "promiseNest": {"sync": None, "promise": None},
+            },
+            "promiseNest": {
+                "sync": None,
+                "promise": None,
+                "syncNest": {"sync": None, "promise": None},
+                "promiseNest": {"sync": None, "promise": None},
+            },
+        }
+
+        @mark.asyncio
+        async def returns_null():
+            result = await execute_query(query, NullingData())
+            assert result == (data, None)
+
+        @mark.asyncio
+        async def throws():
+            result = await execute_query(query, ThrowingData())
+            assert result == (
+                data,
+                [
+                    {
+                        "message": str(sync_error),
+                        "path": ["syncNest", "sync"],
+                        "locations": [(4, 17)],
+                    },
+                    {
+                        "message": str(promise_error),
+                        "path": ["syncNest", "promise"],
+                        "locations": [(5, 17)],
+                    },
+                    {
+                        "message": str(sync_error),
+                        "path": ["syncNest", "syncNest", "sync"],
+                        "locations": [(6, 28)],
+                    },
+                    {
+                        "message": str(promise_error),
+                        "path": ["syncNest", "syncNest", "promise"],
+                        "locations": [(6, 33)],
+                    },
+                    {
+                        "message": str(sync_error),
+                        "path": ["syncNest", "promiseNest", "sync"],
+                        "locations": [(7, 31)],
+                    },
+                    {
+                        "message": str(promise_error),
+                        "path": ["syncNest", "promiseNest", "promise"],
+                        "locations": [(7, 36)],
+                    },
+                    {
+                        "message": str(sync_error),
+                        "path": ["promiseNest", "sync"],
+                        "locations": [(10, 17)],
+                    },
+                    {
+                        "message": str(promise_error),
+                        "path": ["promiseNest", "promise"],
+                        "locations": [(11, 17)],
+                    },
+                    {
+                        "message": str(sync_error),
+                        "path": ["promiseNest", "syncNest", "sync"],
+                        "locations": [(12, 28)],
+                    },
+                    {
+                        "message": str(promise_error),
+                        "path": ["promiseNest", "syncNest", "promise"],
+                        "locations": [(12, 33)],
+                    },
+                    {
+                        "message": str(sync_error),
+                        "path": ["promiseNest", "promiseNest", "sync"],
+                        "locations": [(13, 31)],
+                    },
+                    {
+                        "message": str(promise_error),
+                        "path": ["promiseNest", "promiseNest", "promise"],
+                        "locations": [(13, 36)],
+                    },
+                ],
+            )
+
+    def describe_nulls_first_nullable_after_long_chain_of_non_null_fields():
+        query = """
+            {
+              syncNest {
+                syncNonNullNest {
+                  promiseNonNullNest {
+                    syncNonNullNest {
+                      promiseNonNullNest {
+                        syncNonNull
+                      }
+                    }
+                  }
+                }
+              }
+              promiseNest {
+                syncNonNullNest {
+                  promiseNonNullNest {
+                    syncNonNullNest {
+                      promiseNonNullNest {
+                        syncNonNull
+                      }
+                    }
+                  }
+                }
+              }
+              anotherNest: syncNest {
+                syncNonNullNest {
+                  promiseNonNullNest {
+                    syncNonNullNest {
+                      promiseNonNullNest {
+                        promiseNonNull
+                      }
+                    }
+                  }
+                }
+              }
+              anotherPromiseNest: promiseNest {
+                syncNonNullNest {
+                  promiseNonNullNest {
+                    syncNonNullNest {
+                      promiseNonNullNest {
+                        promiseNonNull
+                      }
+                    }
+                  }
+                }
+              }
+            }
+            """
+        data = {
+            "syncNest": None,
+            "promiseNest": None,
+            "anotherNest": None,
+            "anotherPromiseNest": None,
+        }
+
+        @mark.asyncio
+        async def returns_null():
+            result = await execute_query(query, NullingData())
+            assert result == (
+                data,
+                [
+                    {
+                        "message": "Cannot return null for non-nullable field"
+                        " DataType.syncNonNull.",
+                        "path": [
+                            "syncNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNull",
+                        ],
+                        "locations": [(8, 25)],
+                    },
+                    {
+                        "message": "Cannot return null for non-nullable field"
+                        " DataType.syncNonNull.",
+                        "path": [
+                            "promiseNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNull",
+                        ],
+                        "locations": [(19, 25)],
+                    },
+                    {
+                        "message": "Cannot return null for non-nullable field"
+                        " DataType.promiseNonNull.",
+                        "path": [
+                            "anotherNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "promiseNonNull",
+                        ],
+                        "locations": [(30, 25)],
+                    },
+                    {
+                        "message": "Cannot return null for non-nullable field"
+                        " DataType.promiseNonNull.",
+                        "path": [
+                            "anotherPromiseNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "promiseNonNull",
+                        ],
+                        "locations": [(41, 25)],
+                    },
+                ],
+            )
+
+        @mark.asyncio
+        async def throws():
+            result = await execute_query(query, ThrowingData())
+            assert result == (
+                data,
+                [
+                    {
+                        "message": str(sync_non_null_error),
+                        "path": [
+                            "syncNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNull",
+                        ],
+                        "locations": [(8, 25)],
+                    },
+                    {
+                        "message": str(sync_non_null_error),
+                        "path": [
+                            "promiseNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNull",
+                        ],
+                        "locations": [(19, 25)],
+                    },
+                    {
+                        "message": str(promise_non_null_error),
+                        "path": [
+                            "anotherNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "promiseNonNull",
+                        ],
+                        "locations": [(30, 25)],
+                    },
+                    {
+                        "message": str(promise_non_null_error),
+                        "path": [
+                            "anotherPromiseNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "syncNonNullNest",
+                            "promiseNonNullNest",
+                            "promiseNonNull",
+                        ],
+                        "locations": [(41, 25)],
+                    },
+                ],
+            )
+
+    def describe_nulls_the_top_level_if_non_nullable_field():
+        query = """
+            {
+                syncNonNull
+            }
+            """
+
+        @mark.asyncio
+        async def returns_null():
+            result = await execute_sync_and_async(query, NullingData())
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Cannot return null for non-nullable field"
+                        " DataType.syncNonNull.",
+                        "path": ["syncNonNull"],
+                        "locations": [(3, 17)],
+                    }
+                ],
+            )
+
+        @mark.asyncio
+        async def throws():
+            result = await execute_sync_and_async(query, ThrowingData())
+            assert result == (
+                None,
+                [
+                    {
+                        "message": str(sync_non_null_error),
+                        "path": ["syncNonNull"],
+                        "locations": [(3, 17)],
+                    }
+                ],
+            )
+
+    def describe_handles_non_null_argument():
+
+        # noinspection PyPep8Naming
+        schema_with_non_null_arg = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "withNonNullArg": GraphQLField(
+                        GraphQLString,
+                        args={
+                            "cannotBeNull": GraphQLArgument(
+                                GraphQLNonNull(GraphQLString)
+                            )
+                        },
+                        resolve=lambda _obj, _info, cannotBeNull: "Passed: "
+                        + str(cannotBeNull),
+                    )
+                },
+            )
+        )
+
+        def succeeds_when_passed_non_null_literal_value():
+            result = execute(
+                schema_with_non_null_arg,
+                parse(
+                    """
+                    query {
+                      withNonNullArg (cannotBeNull: "literal value")
+                    }
+                    """
+                ),
+            )
+
+            assert result == ({"withNonNullArg": "Passed: literal value"}, None)
+
+        def succeeds_when_passed_non_null_variable_value():
+            result = execute(
+                schema_with_non_null_arg,
+                parse(
+                    """
+                    query ($testVar: String = "default value") {
+                      withNonNullArg (cannotBeNull: $testVar)
+                    }
+                    """
+                ),
+                variable_values={},
+            )  # intentionally missing variable
+
+            assert result == ({"withNonNullArg": "Passed: default value"}, None)
+
+        def field_error_when_missing_non_null_arg():
+            # Note: validation should identify this issue first
+            # (missing args rule) however execution should still
+            # protect against this.
+            result = execute(
+                schema_with_non_null_arg,
+                parse(
+                    """
+                    query {
+                      withNonNullArg
+                    }
+                    """
+                ),
+            )
+
+            assert result == (
+                {"withNonNullArg": None},
+                [
+                    {
+                        "message": "Argument 'cannotBeNull' of required type"
+                        " 'String!' was not provided.",
+                        "locations": [(3, 23)],
+                        "path": ["withNonNullArg"],
+                    }
+                ],
+            )
+
+        def field_error_when_non_null_arg_provided_null():
+            # Note: validation should identify this issue first
+            # (values of correct type rule) however execution
+            # should still protect against this.
+            result = execute(
+                schema_with_non_null_arg,
+                parse(
+                    """
+                    query {
+                      withNonNullArg(cannotBeNull: null)
+                    }
+                    """
+                ),
+            )
+
+            assert result == (
+                {"withNonNullArg": None},
+                [
+                    {
+                        "message": "Argument 'cannotBeNull' of non-null type"
+                        " 'String!' must not be null.",
+                        "locations": [(3, 52)],
+                        "path": ["withNonNullArg"],
+                    }
+                ],
+            )
+
+        def field_error_when_non_null_arg_not_provided_variable_value():
+            # Note: validation should identify this issue first
+            # (variables in allowed position rule) however execution
+            # should still protect against this.
+            result = execute(
+                schema_with_non_null_arg,
+                parse(
+                    """
+                    query ($testVar: String) {
+                      withNonNullArg(cannotBeNull: $testVar)
+                    }
+                    """
+                ),
+                variable_values={},
+            )  # intentionally missing variable
+
+            assert result == (
+                {"withNonNullArg": None},
+                [
+                    {
+                        "message": "Argument 'cannotBeNull' of required type"
+                        " 'String!' was provided the variable"
+                        " '$testVar' which was not provided"
+                        " a runtime value.",
+                        "locations": [(3, 52)],
+                        "path": ["withNonNullArg"],
+                    }
+                ],
+            )
+
+        def field_error_when_non_null_arg_provided_explicit_null_variable():
+            result = execute(
+                schema_with_non_null_arg,
+                parse(
+                    """
+                    query ($testVar: String = "default value") {
+                      withNonNullArg (cannotBeNull: $testVar)
+                    }
+                    """
+                ),
+                variable_values={"testVar": None},
+            )
+
+            assert result == (
+                {"withNonNullArg": None},
+                [
+                    {
+                        "message": "Argument 'cannotBeNull' of non-null type"
+                        " 'String!' must not be null.",
+                        "locations": [(3, 53)],
+                        "path": ["withNonNullArg"],
+                    }
+                ],
+            )
diff --git a/tests/execution/test_parallel.py b/tests/execution/test_parallel.py
new file mode 100644
index 0000000..f1106f2
--- /dev/null
+++ b/tests/execution/test_parallel.py
@@ -0,0 +1,155 @@
+import asyncio
+from typing import Awaitable
+
+from pytest import mark  # type: ignore
+
+from graphql.execution import execute
+from graphql.language import parse
+from graphql.type import (
+    GraphQLSchema,
+    GraphQLObjectType,
+    GraphQLField,
+    GraphQLList,
+    GraphQLInterfaceType,
+    GraphQLBoolean,
+    GraphQLInt,
+    GraphQLString,
+)
+
+
+class Barrier:
+    """Barrier that makes progress only after a certain number of waits."""
+
+    def __init__(self, number: int):
+        self.event = asyncio.Event()
+        self.number = number
+
+    async def wait(self) -> bool:
+        self.number -= 1
+        if not self.number:
+            self.event.set()
+        return await self.event.wait()
+
+
+def describe_parallel_execution():
+    @mark.asyncio
+    async def resolve_fields_in_parallel():
+        barrier = Barrier(2)
+
+        async def resolve(*_args):
+            return await barrier.wait()
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "foo": GraphQLField(GraphQLBoolean, resolve=resolve),
+                    "bar": GraphQLField(GraphQLBoolean, resolve=resolve),
+                },
+            )
+        )
+
+        ast = parse("{foo, bar}")
+
+        # raises TimeoutError if not parallel
+        awaitable_result = execute(schema, ast)
+        assert isinstance(awaitable_result, Awaitable)
+        result = await asyncio.wait_for(awaitable_result, 1.0)
+
+        assert result == ({"foo": True, "bar": True}, None)
+
+    @mark.asyncio
+    async def resolve_list_in_parallel():
+        barrier = Barrier(2)
+
+        async def resolve(*_args):
+            return await barrier.wait()
+
+        async def resolve_list(*args):
+            return [resolve(*args), resolve(*args)]
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "foo": GraphQLField(
+                        GraphQLList(GraphQLBoolean), resolve=resolve_list
+                    )
+                },
+            )
+        )
+
+        ast = parse("{foo}")
+
+        # raises TimeoutError if not parallel
+        awaitable_result = execute(schema, ast)
+        assert isinstance(awaitable_result, Awaitable)
+        result = await asyncio.wait_for(awaitable_result, 1.0)
+
+        assert result == ({"foo": [True, True]}, None)
+
+    @mark.asyncio
+    async def resolve_is_type_of_in_parallel():
+        FooType = GraphQLInterfaceType("Foo", {"foo": GraphQLField(GraphQLString)})
+
+        barrier = Barrier(4)
+
+        async def is_type_of_bar(obj, *_args):
+            await barrier.wait()
+            return obj["foo"] == "bar"
+
+        BarType = GraphQLObjectType(
+            "Bar",
+            {"foo": GraphQLField(GraphQLString), "foobar": GraphQLField(GraphQLInt)},
+            interfaces=[FooType],
+            is_type_of=is_type_of_bar,
+        )
+
+        async def is_type_of_baz(obj, *_args):
+            await barrier.wait()
+            return obj["foo"] == "baz"
+
+        BazType = GraphQLObjectType(
+            "Baz",
+            {"foo": GraphQLField(GraphQLString), "foobaz": GraphQLField(GraphQLInt)},
+            interfaces=[FooType],
+            is_type_of=is_type_of_baz,
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "foo": GraphQLField(
+                        GraphQLList(FooType),
+                        resolve=lambda *_args: [
+                            {"foo": "bar", "foobar": 1},
+                            {"foo": "baz", "foobaz": 2},
+                        ],
+                    )
+                },
+            ),
+            types=[BarType, BazType],
+        )
+
+        ast = parse(
+            """
+            {
+              foo {
+                foo
+                ... on Bar { foobar }
+                ... on Baz { foobaz }
+              }
+            }
+            """
+        )
+
+        # raises TimeoutError if not parallel
+        awaitable_result = execute(schema, ast)
+        assert isinstance(awaitable_result, Awaitable)
+        result = await asyncio.wait_for(awaitable_result, 1.0)
+
+        assert result == (
+            {"foo": [{"foo": "bar", "foobar": 1}, {"foo": "baz", "foobaz": 2}]},
+            None,
+        )
diff --git a/tests/execution/test_resolve.py b/tests/execution/test_resolve.py
new file mode 100644
index 0000000..989e229
--- /dev/null
+++ b/tests/execution/test_resolve.py
@@ -0,0 +1,182 @@
+from graphql import graphql_sync
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+
+
+def describe_execute_resolve_function():
+    def _test_schema(test_field):
+        return GraphQLSchema(GraphQLObjectType("Query", {"test": test_field}))
+
+    def default_function_accesses_attributes():
+        class RootValue:
+            test = "testValue"
+
+        assert graphql_sync(
+            schema=_test_schema(GraphQLField(GraphQLString)),
+            source="{ test }",
+            root_value=RootValue(),
+        ) == ({"test": "testValue"}, None,)
+
+    def default_function_accesses_keys():
+        root_value = {"test": "testValue"}
+
+        assert graphql_sync(
+            schema=_test_schema(GraphQLField(GraphQLString)),
+            source="{ test }",
+            root_value=root_value,
+        ) == ({"test": "testValue"}, None)
+
+    def default_function_calls_methods():
+        class RootValue:
+            _secret = "secretValue"
+
+            def test(self, _info):
+                return self._secret
+
+        assert graphql_sync(
+            schema=_test_schema(GraphQLField(GraphQLString)),
+            source="{ test }",
+            root_value=RootValue(),
+        ) == ({"test": "secretValue"}, None,)
+
+    def default_function_passes_args_and_context():
+        class Adder:
+            _num: int
+
+            def __init__(self, num):
+                self._num = num
+
+            def test(self, info, addend1):
+                return self._num + addend1 + info.context.addend2
+
+        root_value = Adder(700)
+
+        schema = _test_schema(
+            GraphQLField(GraphQLInt, args={"addend1": GraphQLArgument(GraphQLInt)})
+        )
+
+        class ContextValue:
+            addend2 = 9
+
+        context_value = ContextValue()
+        source = "{ test(addend1: 80) }"
+
+        assert graphql_sync(
+            schema=schema,
+            source=source,
+            root_value=root_value,
+            context_value=context_value,
+        ) == ({"test": 789}, None,)
+
+    def uses_provided_resolve_function():
+        schema = _test_schema(
+            GraphQLField(
+                GraphQLString,
+                args={
+                    "aStr": GraphQLArgument(GraphQLString),
+                    "aInt": GraphQLArgument(GraphQLInt),
+                },
+                resolve=lambda source, info, **args: repr([source, args]),
+            )
+        )
+
+        def execute(source, root_value=None, context_value=None):
+            return graphql_sync(
+                schema=schema,
+                source=source,
+                root_value=root_value,
+                context_value=context_value,
+            )
+
+        assert execute("{ test }") == ({"test": "[None, {}]"}, None)
+
+        assert execute("{ test }", "Source!") == ({"test": "['Source!', {}]"}, None,)
+
+        assert execute('{ test(aStr: "String!") }', "Source!") == (
+            {"test": "['Source!', {'aStr': 'String!'}]"},
+            None,
+        )
+
+        assert execute('{ test(aInt: -123, aStr: "String!") }', "Source!") == (
+            {"test": "['Source!', {'aStr': 'String!', 'aInt': -123}]"},
+            None,
+        )
+
+    def transforms_arguments_using_out_names():
+        # This is an extension of GraphQL.js.
+        schema = _test_schema(
+            GraphQLField(
+                GraphQLString,
+                args={
+                    "aStr": GraphQLArgument(GraphQLString, out_name="a_str"),
+                    "aInt": GraphQLArgument(GraphQLInt, out_name="a_int"),
+                },
+                resolve=lambda source, info, **args: repr([source, args]),
+            )
+        )
+
+        def execute(source, root_value=None):
+            return graphql_sync(schema=schema, source=source, root_value=root_value)
+
+        assert execute("{ test }") == ({"test": "[None, {}]"}, None)
+
+        assert execute("{ test }", "Source!") == ({"test": "['Source!', {}]"}, None,)
+
+        assert execute('{ test(aStr: "String!") }', "Source!") == (
+            {"test": "['Source!', {'a_str': 'String!'}]"},
+            None,
+        )
+
+        assert execute('{ test(aInt: -123, aStr: "String!") }', "Source!") == (
+            {"test": "['Source!', {'a_str': 'String!', 'a_int': -123}]"},
+            None,
+        )
+
+    def transforms_arguments_with_inputs_using_out_names():
+        # This is an extension of GraphQL.js.
+        TestInputObject = GraphQLInputObjectType(
+            "TestInputObjectType",
+            lambda: {
+                "inputOne": GraphQLInputField(GraphQLString, out_name="input_one"),
+                "inputRecursive": GraphQLInputField(
+                    TestInputObject, out_name="input_recursive"
+                ),
+            },
+        )
+
+        schema = _test_schema(
+            GraphQLField(
+                GraphQLString,
+                args={"aInput": GraphQLArgument(TestInputObject, out_name="a_input")},
+                resolve=lambda source, info, **args: repr([source, args]),
+            )
+        )
+
+        def execute(source, root_value=None):
+            return graphql_sync(schema=schema, source=source, root_value=root_value,)
+
+        assert execute("{ test }") == ({"test": "[None, {}]"}, None)
+
+        assert execute('{ test(aInput: {inputOne: "String!"}) }', "Source!") == (
+            {"test": "['Source!', {'a_input': {'input_one': 'String!'}}]"},
+            None,
+        )
+
+        assert execute(
+            '{ test(aInput: {inputRecursive: {inputOne: "SourceRecursive!"}}) }',
+            "Source!",
+        ) == (
+            {
+                "test": "['Source!',"
+                " {'a_input': {'input_recursive': {'input_one': 'SourceRecursive!'}}}]"
+            },
+            None,
+        )
diff --git a/tests/execution/test_schema.py b/tests/execution/test_schema.py
new file mode 100644
index 0000000..d7c383d
--- /dev/null
+++ b/tests/execution/test_schema.py
@@ -0,0 +1,182 @@
+from graphql.execution import execute
+from graphql.language import parse
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLField,
+    GraphQLID,
+    GraphQLInt,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+
+
+def describe_execute_handles_execution_with_a_complex_schema():
+    def executes_using_a_schema():
+        class Article:
+
+            # noinspection PyShadowingBuiltins
+            def __init__(self, id):
+                self.id = id
+                self.isPublished = True
+                self.author = JohnSmith()
+                self.title = f"My Article {id}"
+                self.body = "This is a post"
+                self.hidden = "This data is not exposed in the schema"
+                self.keywords = ["foo", "bar", 1, True, None]
+
+        BlogImage = GraphQLObjectType(
+            "Image",
+            {
+                "url": GraphQLField(GraphQLString),
+                "width": GraphQLField(GraphQLInt),
+                "height": GraphQLField(GraphQLInt),
+            },
+        )
+
+        BlogArticle: GraphQLObjectType
+
+        BlogAuthor = GraphQLObjectType(
+            "Author",
+            lambda: {
+                "id": GraphQLField(GraphQLString),
+                "name": GraphQLField(GraphQLString),
+                "pic": GraphQLField(
+                    BlogImage,
+                    args={
+                        "width": GraphQLArgument(GraphQLInt),
+                        "height": GraphQLArgument(GraphQLInt),
+                    },
+                    resolve=lambda obj, info, width, height: obj.pic(
+                        info, width, height
+                    ),
+                ),
+                "recentArticle": GraphQLField(BlogArticle),
+            },
+        )
+
+        BlogArticle = GraphQLObjectType(
+            "Article",
+            {
+                "id": GraphQLField(GraphQLNonNull(GraphQLString)),
+                "isPublished": GraphQLField(GraphQLBoolean),
+                "author": GraphQLField(BlogAuthor),
+                "title": GraphQLField(GraphQLString),
+                "body": GraphQLField(GraphQLString),
+                "keywords": GraphQLField(GraphQLList(GraphQLString)),
+            },
+        )
+
+        # noinspection PyShadowingBuiltins
+        BlogQuery = GraphQLObjectType(
+            "Query",
+            {
+                "article": GraphQLField(
+                    BlogArticle,
+                    args={"id": GraphQLArgument(GraphQLID)},
+                    resolve=lambda obj, info, id: Article(id),
+                ),
+                "feed": GraphQLField(
+                    GraphQLList(BlogArticle),
+                    resolve=lambda *_args: [Article(n + 1) for n in range(10)],
+                ),
+            },
+        )
+
+        BlogSchema = GraphQLSchema(BlogQuery)
+
+        # noinspection PyPep8Naming,PyMethodMayBeStatic
+        class Author:
+            def pic(self, info_, width, height):
+                return Pic(123, width, height)
+
+            @property
+            def recentArticle(self):
+                return Article(1)
+
+        class JohnSmith(Author):
+            id = 123
+            name = "John Smith"
+
+        class Pic:
+            def __init__(self, uid, width, height):
+                self.url = f"cdn://{uid}"
+                self.width = f"{width}"
+                self.height = f"{height}"
+
+        document = parse(
+            """
+            {
+              feed {
+                id,
+                title
+              },
+              article(id: "1") {
+                ...articleFields,
+                author {
+                  id,
+                  name,
+                  pic(width: 640, height: 480) {
+                    url,
+                    width,
+                    height
+                  },
+                  recentArticle {
+                    ...articleFields,
+                    keywords
+                  }
+                }
+              }
+            }
+
+            fragment articleFields on Article {
+              id,
+              isPublished,
+              title,
+              body,
+              hidden,
+              notDefined
+            }
+            """
+        )
+
+        # Note: this is intentionally not validating to ensure appropriate
+        # behavior occurs when executing an invalid query.
+        assert execute(schema=BlogSchema, document=document) == (
+            {
+                "feed": [
+                    {"id": "1", "title": "My Article 1"},
+                    {"id": "2", "title": "My Article 2"},
+                    {"id": "3", "title": "My Article 3"},
+                    {"id": "4", "title": "My Article 4"},
+                    {"id": "5", "title": "My Article 5"},
+                    {"id": "6", "title": "My Article 6"},
+                    {"id": "7", "title": "My Article 7"},
+                    {"id": "8", "title": "My Article 8"},
+                    {"id": "9", "title": "My Article 9"},
+                    {"id": "10", "title": "My Article 10"},
+                ],
+                "article": {
+                    "id": "1",
+                    "isPublished": True,
+                    "title": "My Article 1",
+                    "body": "This is a post",
+                    "author": {
+                        "id": "123",
+                        "name": "John Smith",
+                        "pic": {"url": "cdn://123", "width": 640, "height": 480},
+                        "recentArticle": {
+                            "id": "1",
+                            "isPublished": True,
+                            "title": "My Article 1",
+                            "body": "This is a post",
+                            "keywords": ["foo", "bar", "1", "true", None],
+                        },
+                    },
+                },
+            },
+            None,
+        )
diff --git a/tests/execution/test_sync.py b/tests/execution/test_sync.py
new file mode 100644
index 0000000..283989e
--- /dev/null
+++ b/tests/execution/test_sync.py
@@ -0,0 +1,132 @@
+from gc import collect
+from inspect import isawaitable
+from typing import Awaitable, cast
+
+from pytest import mark, raises  # type: ignore
+
+from graphql import graphql_sync
+from graphql.execution import execute
+from graphql.language import parse
+from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
+from graphql.validation import validate
+
+
+def describe_execute_synchronously_when_possible():
+    def _resolve_sync(root_value, _info):
+        return root_value
+
+    async def _resolve_async(root_value, _info):
+        return root_value
+
+    schema = GraphQLSchema(
+        GraphQLObjectType(
+            "Query",
+            {
+                "syncField": GraphQLField(GraphQLString, resolve=_resolve_sync),
+                "asyncField": GraphQLField(GraphQLString, resolve=_resolve_async),
+            },
+        ),
+        GraphQLObjectType(
+            "Mutation",
+            {"syncMutationField": GraphQLField(GraphQLString, resolve=_resolve_sync)},
+        ),
+    )
+
+    def does_not_return_a_promise_for_initial_errors():
+        doc = "fragment Example on Query { syncField }"
+        assert execute(schema, parse(doc), "rootValue") == (
+            None,
+            [{"message": "Must provide an operation."}],
+        )
+
+    def does_not_return_a_promise_if_fields_are_all_synchronous():
+        doc = "query Example { syncField }"
+        assert execute(schema, parse(doc), "rootValue") == (
+            {"syncField": "rootValue"},
+            None,
+        )
+
+    def does_not_return_a_promise_if_mutation_fields_are_all_synchronous():
+        doc = "mutation Example { syncMutationField }"
+        assert execute(schema, parse(doc), "rootValue") == (
+            {"syncMutationField": "rootValue"},
+            None,
+        )
+
+    @mark.asyncio
+    async def returns_a_promise_if_any_field_is_asynchronous():
+        doc = "query Example { syncField, asyncField }"
+        result = execute(schema, parse(doc), "rootValue")
+        assert isawaitable(result)
+        result = cast(Awaitable, result)
+        assert await result == (
+            {"syncField": "rootValue", "asyncField": "rootValue"},
+            None,
+        )
+
+    def describe_graphql_sync():
+        def reports_errors_raised_during_schema_validation():
+            bad_schema = GraphQLSchema()
+            result = graphql_sync(schema=bad_schema, source="{ __typename }")
+            assert result == (None, [{"message": "Query root type must be provided."}])
+
+        def does_not_return_a_promise_for_syntax_errors():
+            doc = "fragment Example on Query { { { syncField }"
+            assert graphql_sync(schema, doc) == (
+                None,
+                [
+                    {
+                        "message": "Syntax Error: Expected Name, found '{'.",
+                        "locations": [(1, 29)],
+                    }
+                ],
+            )
+
+        def does_not_return_a_promise_for_validation_errors():
+            doc = "fragment Example on Query { unknownField }"
+            validation_errors = validate(schema, parse(doc))
+            result = graphql_sync(schema, doc)
+            assert result == (None, validation_errors)
+
+        def raises_a_type_error_when_no_query_is_passed():
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                assert graphql_sync(schema, None)  # type: ignore
+            msg = str(exc_info.value)
+            assert msg == "Must provide Source. Received: None."
+
+        def does_not_return_a_promise_for_sync_execution():
+            doc = "query Example { syncField }"
+            assert graphql_sync(schema, doc, "rootValue") == (
+                {"syncField": "rootValue"},
+                None,
+            )
+
+        @mark.asyncio
+        @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+        async def throws_if_encountering_async_operation_with_check_sync():
+            doc = "query Example { syncField, asyncField }"
+            with raises(RuntimeError) as exc_info:
+                graphql_sync(schema, doc, "rootValue", check_sync=True)
+            msg = str(exc_info.value)
+            assert msg == "GraphQL execution failed to complete synchronously."
+
+        @mark.asyncio
+        @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+        async def throws_if_encountering_async_operation_without_check_sync():
+            doc = "query Example { syncField, asyncField }"
+            result = graphql_sync(schema, doc, "rootValue")
+            assert result == (
+                {"syncField": "rootValue", "asyncField": None},
+                [
+                    {
+                        "message": "String cannot represent value:"
+                        " <coroutine _resolve_async>",
+                        "locations": [(1, 28)],
+                        "path": ["asyncField"],
+                    }
+                ],
+            )
+            # garbage collect coroutine in order to not postpone the warning
+            del result
+            collect()
diff --git a/tests/execution/test_union_interface.py b/tests/execution/test_union_interface.py
new file mode 100644
index 0000000..fcef479
--- /dev/null
+++ b/tests/execution/test_union_interface.py
@@ -0,0 +1,527 @@
+from typing import Optional, Union, List
+
+from graphql.execution import execute
+from graphql.language import parse
+from graphql.type import (
+    GraphQLBoolean,
+    GraphQLField,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+    GraphQLUnionType,
+)
+
+
+class Dog:
+
+    name: str
+    barks: bool
+    mother: Optional["Dog"]
+    father: Optional["Dog"]
+    progeny: List["Dog"]
+
+    def __init__(self, name: str, barks: bool):
+        self.name = name
+        self.barks = barks
+        self.mother = None
+        self.father = None
+        self.progeny = []
+
+
+class Cat:
+
+    name: str
+    meows: bool
+    mother: Optional["Cat"]
+    father: Optional["Cat"]
+    progeny: List["Cat"]
+
+    def __init__(self, name: str, meows: bool):
+        self.name = name
+        self.meows = meows
+        self.mother = None
+        self.father = None
+        self.progeny = []
+
+
+class Person:
+
+    name: str
+    pets: Optional[List[Union[Dog, Cat]]]
+    friends: Optional[List[Union[Dog, Cat, "Person"]]]
+
+    def __init__(
+        self,
+        name: str,
+        pets: Optional[List[Union[Dog, Cat]]] = None,
+        friends: Optional[List[Union[Dog, Cat, "Person"]]] = None,
+    ):
+        self.name = name
+        self.pets = pets
+        self.friends = friends
+
+
+NamedType = GraphQLInterfaceType("Named", {"name": GraphQLField(GraphQLString)})
+
+LifeType = GraphQLInterfaceType(
+    "Life", lambda: {"progeny": GraphQLField(GraphQLList(LifeType))}  # type: ignore
+)
+
+MammalType = GraphQLInterfaceType(
+    "Mammal",
+    lambda: {
+        "progeny": GraphQLField(GraphQLList(MammalType)),  # type: ignore
+        "mother": GraphQLField(MammalType),  # type: ignore
+        "father": GraphQLField(MammalType),  # type: ignore
+    },
+    interfaces=[LifeType],
+)
+
+DogType = GraphQLObjectType(
+    "Dog",
+    lambda: {
+        "name": GraphQLField(GraphQLString),
+        "barks": GraphQLField(GraphQLBoolean),
+        "progeny": GraphQLField(GraphQLList(DogType)),  # type: ignore
+        "mother": GraphQLField(DogType),  # type: ignore
+        "father": GraphQLField(DogType),  # type: ignore
+    },
+    interfaces=[MammalType, LifeType, NamedType],
+    is_type_of=lambda value, info: isinstance(value, Dog),
+)
+
+CatType = GraphQLObjectType(
+    "Cat",
+    lambda: {
+        "name": GraphQLField(GraphQLString),
+        "meows": GraphQLField(GraphQLBoolean),
+        "progeny": GraphQLField(GraphQLList(CatType)),  # type: ignore
+        "mother": GraphQLField(CatType),  # type: ignore
+        "father": GraphQLField(CatType),  # type: ignore
+    },
+    interfaces=[MammalType, LifeType, NamedType],
+    is_type_of=lambda value, info: isinstance(value, Cat),
+)
+
+
+def resolve_pet_type(value, _info, _type):
+    if isinstance(value, Dog):
+        return DogType
+    if isinstance(value, Cat):
+        return CatType
+
+    # Not reachable. All possible types have been considered.
+    raise TypeError("Unexpected pet type")
+
+
+PetType = GraphQLUnionType("Pet", [DogType, CatType], resolve_type=resolve_pet_type)
+
+PersonType = GraphQLObjectType(
+    "Person",
+    lambda: {
+        "name": GraphQLField(GraphQLString),
+        "pets": GraphQLField(GraphQLList(PetType)),
+        "friends": GraphQLField(GraphQLList(NamedType)),
+        "progeny": GraphQLField(GraphQLList(PersonType)),  # type: ignore
+        "mother": GraphQLField(PersonType),  # type: ignore
+        "father": GraphQLField(PersonType),  # type: ignore
+    },
+    interfaces=[NamedType, MammalType, LifeType],
+    is_type_of=lambda value, _info: isinstance(value, Person),
+)
+
+schema = GraphQLSchema(PersonType, types=[PetType])
+
+garfield = Cat("Garfield", False)
+garfield.mother = Cat("Garfield's Mom", False)
+garfield.mother.progeny = [garfield]
+
+odie = Dog("Odie", True)
+odie.mother = Dog("Odie's Mom", True)
+odie.mother.progeny = [odie]
+
+liz = Person("Liz", [], [])
+john = Person("John", [garfield, odie], [liz, odie])
+
+
+def describe_execute_union_and_intersection_types():
+    def can_introspect_on_union_and_intersection_types():
+        document = parse(
+            """
+            {
+              Named: __type(name: "Named") {
+                kind
+                name
+                fields { name }
+                interfaces { name }
+                possibleTypes { name }
+                enumValues { name }
+                inputFields { name }
+              }
+              Mammal: __type(name: "Mammal") {
+                kind
+                name
+                fields { name }
+                interfaces { name }
+                possibleTypes { name }
+                enumValues { name }
+                inputFields { name }
+              }
+              Pet: __type(name: "Pet") {
+                kind
+                name
+                fields { name }
+                interfaces { name }
+                possibleTypes { name }
+                enumValues { name }
+                inputFields { name }
+              }
+            }
+            """
+        )
+
+        assert execute(schema=schema, document=document) == (
+            {
+                "Named": {
+                    "kind": "INTERFACE",
+                    "name": "Named",
+                    "fields": [{"name": "name"}],
+                    "interfaces": [],
+                    "possibleTypes": [
+                        {"name": "Dog"},
+                        {"name": "Cat"},
+                        {"name": "Person"},
+                    ],
+                    "enumValues": None,
+                    "inputFields": None,
+                },
+                "Mammal": {
+                    "kind": "INTERFACE",
+                    "name": "Mammal",
+                    "fields": [
+                        {"name": "progeny"},
+                        {"name": "mother"},
+                        {"name": "father"},
+                    ],
+                    "interfaces": [{"name": "Life"}],
+                    "possibleTypes": [
+                        {"name": "Dog"},
+                        {"name": "Cat"},
+                        {"name": "Person"},
+                    ],
+                    "enumValues": None,
+                    "inputFields": None,
+                },
+                "Pet": {
+                    "kind": "UNION",
+                    "name": "Pet",
+                    "fields": None,
+                    "interfaces": None,
+                    "possibleTypes": [{"name": "Dog"}, {"name": "Cat"}],
+                    "enumValues": None,
+                    "inputFields": None,
+                },
+            },
+            None,
+        )
+
+    def executes_using_union_types():
+        # NOTE: This is an *invalid* query, but it should be *executable*.
+        document = parse(
+            """
+            {
+              __typename
+              name
+              pets {
+                __typename
+                name
+                barks
+                meows
+              }
+            }
+            """
+        )
+
+        assert execute(schema=schema, document=document, root_value=john) == (
+            {
+                "__typename": "Person",
+                "name": "John",
+                "pets": [
+                    {"__typename": "Cat", "name": "Garfield", "meows": False},
+                    {"__typename": "Dog", "name": "Odie", "barks": True},
+                ],
+            },
+            None,
+        )
+
+    def executes_union_types_with_inline_fragment():
+        # This is the valid version of the query in the above test.
+        document = parse(
+            """
+            {
+              __typename
+              name
+              pets {
+                __typename
+                ... on Dog {
+                  name
+                  barks
+                }
+                ... on Cat {
+                  name
+                  meows
+                }
+              }
+            }
+            """
+        )
+
+        assert execute(schema=schema, document=document, root_value=john) == (
+            {
+                "__typename": "Person",
+                "name": "John",
+                "pets": [
+                    {"__typename": "Cat", "name": "Garfield", "meows": False},
+                    {"__typename": "Dog", "name": "Odie", "barks": True},
+                ],
+            },
+            None,
+        )
+
+    def executes_using_interface_types():
+        # NOTE: This is an *invalid* query, but it should be a *executable*.
+        document = parse(
+            """
+            {
+              __typename
+              name
+              friends {
+                __typename
+                name
+                barks
+                meows
+              }
+            }
+            """
+        )
+
+        assert execute(schema=schema, document=document, root_value=john) == (
+            {
+                "__typename": "Person",
+                "name": "John",
+                "friends": [
+                    {"__typename": "Person", "name": "Liz"},
+                    {"__typename": "Dog", "name": "Odie", "barks": True},
+                ],
+            },
+            None,
+        )
+
+    def executes_interface_types_with_inline_fragment():
+        # This is the valid version of the query in the above test.
+        document = parse(
+            """
+            {
+              __typename
+              name
+              friends {
+                __typename
+                name
+                ... on Dog {
+                  barks
+                }
+                ... on Cat {
+                  meows
+                }
+
+                ... on Mammal {
+                  mother {
+                    __typename
+                    ... on Dog {
+                      name
+                      barks
+                    }
+                    ... on Cat {
+                      name
+                      meows
+                    }
+                  }
+                }
+              }
+            }
+            """
+        )
+
+        assert execute(schema=schema, document=document, root_value=john) == (
+            {
+                "__typename": "Person",
+                "name": "John",
+                "friends": [
+                    {"__typename": "Person", "name": "Liz", "mother": None},
+                    {
+                        "__typename": "Dog",
+                        "name": "Odie",
+                        "barks": True,
+                        "mother": {
+                            "__typename": "Dog",
+                            "name": "Odie's Mom",
+                            "barks": True,
+                        },
+                    },
+                ],
+            },
+            None,
+        )
+
+    def executes_interface_types_with_named_fragments():
+        document = parse(
+            """
+            {
+              __typename
+              name
+              friends {
+                __typename
+                name
+                ...DogBarks
+                ...CatMeows
+              }
+            }
+
+            fragment  DogBarks on Dog {
+              barks
+            }
+
+            fragment  CatMeows on Cat {
+              meows
+            }
+            """
+        )
+
+        assert execute(schema=schema, document=document, root_value=john) == (
+            {
+                "__typename": "Person",
+                "name": "John",
+                "friends": [
+                    {"__typename": "Person", "name": "Liz"},
+                    {"__typename": "Dog", "name": "Odie", "barks": True},
+                ],
+            },
+            None,
+        )
+
+    def allows_fragment_conditions_to_be_abstract_types():
+        document = parse(
+            """
+            {
+              __typename
+              name
+              pets {
+                ...PetFields,
+                ...on Mammal {
+                  mother {
+                    ...ProgenyFields
+                  }
+                }
+              }
+              friends { ...FriendFields }
+            }
+
+            fragment PetFields on Pet {
+              __typename
+              ... on Dog {
+                name
+                barks
+              }
+              ... on Cat {
+                name
+                meows
+              }
+            }
+
+            fragment FriendFields on Named {
+              __typename
+              name
+              ... on Dog {
+                barks
+              }
+              ... on Cat {
+                meows
+              }
+            }
+
+            fragment ProgenyFields on Life {
+              progeny {
+                __typename
+              }
+            }
+            """
+        )
+
+        assert execute(schema=schema, document=document, root_value=john) == (
+            {
+                "__typename": "Person",
+                "name": "John",
+                "pets": [
+                    {
+                        "__typename": "Cat",
+                        "name": "Garfield",
+                        "meows": False,
+                        "mother": {"progeny": [{"__typename": "Cat"}]},
+                    },
+                    {
+                        "__typename": "Dog",
+                        "name": "Odie",
+                        "barks": True,
+                        "mother": {"progeny": [{"__typename": "Dog"}]},
+                    },
+                ],
+                "friends": [
+                    {"__typename": "Person", "name": "Liz"},
+                    {"__typename": "Dog", "name": "Odie", "barks": True},
+                ],
+            },
+            None,
+        )
+
+    # noinspection PyPep8Naming
+    def gets_execution_info_in_resolver():
+        encountered = {}
+
+        def resolve_type(_source, info, _type):
+            encountered["context"] = info.context
+            encountered["schema"] = info.schema
+            encountered["root_value"] = info.root_value
+            return PersonType2
+
+        NamedType2 = GraphQLInterfaceType(
+            "Named", {"name": GraphQLField(GraphQLString)}, resolve_type=resolve_type
+        )
+
+        PersonType2 = GraphQLObjectType(
+            "Person",
+            {
+                "name": GraphQLField(GraphQLString),
+                "friends": GraphQLField(GraphQLList(NamedType2)),
+            },
+            interfaces=[NamedType2],
+        )
+
+        schema2 = GraphQLSchema(PersonType2)
+        document = parse("{ name, friends { name } }")
+        root_value = Person("John", [], [liz])
+        context_value = {"authToken": "123abc"}
+
+        assert execute(
+            schema=schema2,
+            document=document,
+            root_value=root_value,
+            context_value=context_value,
+        ) == ({"name": "John", "friends": [{"name": "Liz"}]}, None,)
+
+        assert encountered == {
+            "schema": schema2,
+            "root_value": root_value,
+            "context": context_value,
+        }
diff --git a/tests/execution/test_variables.py b/tests/execution/test_variables.py
new file mode 100644
index 0000000..6e982a8
--- /dev/null
+++ b/tests/execution/test_variables.py
@@ -0,0 +1,1018 @@
+from math import nan
+
+from graphql.execution import execute
+from graphql.execution.values import get_variable_values
+from graphql.language import parse, OperationDefinitionNode, StringValueNode, ValueNode
+from graphql.pyutils import Undefined
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLFloat,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLScalarType,
+    GraphQLSchema,
+    GraphQLString,
+)
+
+
+def parse_serialized_value(value: str) -> str:
+    assert value == "SerializedValue"
+    return "DeserializedValue"
+
+
+def parse_literal_value(ast: ValueNode, _variables=None) -> str:
+    assert isinstance(ast, StringValueNode)
+    return parse_serialized_value(ast.value)
+
+
+TestComplexScalar = GraphQLScalarType(
+    name="ComplexScalar",
+    parse_value=parse_serialized_value,
+    parse_literal=parse_literal_value,
+)
+
+
+TestInputObject = GraphQLInputObjectType(
+    "TestInputObject",
+    {
+        "a": GraphQLInputField(GraphQLString),
+        "b": GraphQLInputField(GraphQLList(GraphQLString)),
+        "c": GraphQLInputField(GraphQLNonNull(GraphQLString)),
+        "d": GraphQLInputField(TestComplexScalar),
+    },
+)
+
+TestCustomInputObject = GraphQLInputObjectType(
+    "TestCustomInputObject",
+    {"x": GraphQLInputField(GraphQLFloat), "y": GraphQLInputField(GraphQLFloat)},
+    out_type=lambda value: f"(x|y) = ({value['x']}|{value['y']})",
+)
+
+
+TestNestedInputObject = GraphQLInputObjectType(
+    "TestNestedInputObject",
+    {
+        "na": GraphQLInputField(GraphQLNonNull(TestInputObject)),
+        "nb": GraphQLInputField(GraphQLNonNull(GraphQLString)),
+    },
+)
+
+
+TestEnum = GraphQLEnumType(
+    "TestEnum",
+    {
+        "NULL": None,
+        "UNDEFINED": Undefined,
+        "NAN": nan,
+        "FALSE": False,
+        "CUSTOM": "custom value",
+        "DEFAULT_VALUE": GraphQLEnumValue(),
+    },
+)
+
+
+def field_with_input_arg(input_arg: GraphQLArgument):
+    return GraphQLField(
+        GraphQLString,
+        args={"input": input_arg},
+        resolve=lambda _obj, _info, **args: repr(args["input"])
+        if "input" in args
+        else None,
+    )
+
+
+TestType = GraphQLObjectType(
+    "TestType",
+    {
+        "fieldWithEnumInput": field_with_input_arg(GraphQLArgument(TestEnum)),
+        "fieldWithNonNullableEnumInput": field_with_input_arg(
+            GraphQLArgument(GraphQLNonNull(TestEnum))
+        ),
+        "fieldWithObjectInput": field_with_input_arg(GraphQLArgument(TestInputObject)),
+        "fieldWithCustomObjectInput": field_with_input_arg(
+            GraphQLArgument(TestCustomInputObject)
+        ),
+        "fieldWithNullableStringInput": field_with_input_arg(
+            GraphQLArgument(GraphQLString)
+        ),
+        "fieldWithNonNullableStringInput": field_with_input_arg(
+            GraphQLArgument(GraphQLNonNull(GraphQLString))
+        ),
+        "fieldWithDefaultArgumentValue": field_with_input_arg(
+            GraphQLArgument(GraphQLString, default_value="Hello World")
+        ),
+        "fieldWithNonNullableStringInputAndDefaultArgValue": field_with_input_arg(
+            GraphQLArgument(GraphQLNonNull(GraphQLString), default_value="Hello World")
+        ),
+        "fieldWithNestedInputObject": field_with_input_arg(
+            GraphQLArgument(TestNestedInputObject, default_value="Hello World")
+        ),
+        "list": field_with_input_arg(GraphQLArgument(GraphQLList(GraphQLString))),
+        "nnList": field_with_input_arg(
+            GraphQLArgument(GraphQLNonNull(GraphQLList(GraphQLString)))
+        ),
+        "listNN": field_with_input_arg(
+            GraphQLArgument(GraphQLList(GraphQLNonNull(GraphQLString)))
+        ),
+        "nnListNN": field_with_input_arg(
+            GraphQLArgument(GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString))))
+        ),
+    },
+)
+
+schema = GraphQLSchema(TestType)
+
+
+def execute_query(query, variable_values=None):
+    document = parse(query)
+    return execute(schema, document, variable_values=variable_values)
+
+
+def describe_execute_handles_inputs():
+    def describe_handles_objects_and_nullability():
+        def describe_using_inline_struct():
+            def executes_with_complex_input():
+                result = execute_query(
+                    """
+                    {
+                      fieldWithObjectInput(
+                        input: {a: "foo", b: ["bar"], c: "baz"})
+                    }
+                    """
+                )
+
+                assert result == (
+                    {"fieldWithObjectInput": "{'a': 'foo', 'b': ['bar'], 'c': 'baz'}"},
+                    None,
+                )
+
+            def executes_with_custom_input():
+                # This is an extension of GraphQL.js.
+                result = execute_query(
+                    """
+                    {
+                      fieldWithCustomObjectInput(
+                        input: {x: -3.0, y: 4.5})
+                    }
+                    """
+                )
+
+                assert result == (
+                    {"fieldWithCustomObjectInput": "'(x|y) = (-3.0|4.5)'"},
+                    None,
+                )
+
+            def properly_parses_single_value_to_list():
+                result = execute_query(
+                    """
+                    {
+                      fieldWithObjectInput(input: {a: "foo", b: "bar", c: "baz"})
+                    }
+                    """
+                )
+
+                assert result == (
+                    {"fieldWithObjectInput": "{'a': 'foo', 'b': ['bar'], 'c': 'baz'}"},
+                    None,
+                )
+
+            def properly_parses_null_value_to_null():
+                result = execute_query(
+                    """
+                    {
+                      fieldWithObjectInput(
+                        input: {a: null, b: null, c: "C", d: null})
+                    }
+                    """
+                )
+
+                assert result == (
+                    {
+                        "fieldWithObjectInput": "{'a': None, 'b': None,"
+                        " 'c': 'C', 'd': None}"
+                    },
+                    None,
+                )
+
+            def properly_parses_null_value_in_list():
+                result = execute_query(
+                    """
+                    {
+                      fieldWithObjectInput(input: {b: ["A",null,"C"], c: "C"})
+                    }
+                    """
+                )
+
+                assert result == (
+                    {"fieldWithObjectInput": "{'b': ['A', None, 'C'], 'c': 'C'}"},
+                    None,
+                )
+
+            def does_not_use_incorrect_value():
+                result = execute_query(
+                    """
+                    {
+                      fieldWithObjectInput(input: ["foo", "bar", "baz"])
+                    }
+                    """
+                )
+
+                assert result == (
+                    {"fieldWithObjectInput": None},
+                    [
+                        {
+                            "message": "Argument 'input' has invalid value"
+                            ' ["foo", "bar", "baz"].',
+                            "path": ["fieldWithObjectInput"],
+                            "locations": [(3, 51)],
+                        }
+                    ],
+                )
+
+            def properly_runs_parse_literal_on_complex_scalar_types():
+                result = execute_query(
+                    """
+                    {
+                      fieldWithObjectInput(input: {c: "foo", d: "SerializedValue"})
+                    }
+                    """
+                )
+
+                assert result == (
+                    {"fieldWithObjectInput": "{'c': 'foo', 'd': 'DeserializedValue'}"},
+                    None,
+                )
+
+        def describe_using_variables():
+            doc = """
+                query ($input: TestInputObject) {
+                  fieldWithObjectInput(input: $input)
+                }
+                """
+
+            def executes_with_complex_input():
+                params = {"input": {"a": "foo", "b": ["bar"], "c": "baz"}}
+                result = execute_query(doc, params)
+
+                assert result == (
+                    {"fieldWithObjectInput": "{'a': 'foo', 'b': ['bar'], 'c': 'baz'}"},
+                    None,
+                )
+
+            def uses_undefined_when_variable_not_provided():
+                result = execute_query(
+                    """
+                    query q($input: String) {
+                      fieldWithNullableStringInput(input: $input)
+                    }
+                    """,
+                    {},
+                )  # Intentionally missing variable values.
+
+                assert result == ({"fieldWithNullableStringInput": None}, None)
+
+            def uses_null_when_variable_provided_explicit_null_value():
+                result = execute_query(
+                    """
+                    query q($input: String) {
+                      fieldWithNullableStringInput(input: $input)
+                    }
+                    """,
+                    {"input": None},
+                )
+
+                assert result == ({"fieldWithNullableStringInput": "None"}, None)
+
+            def uses_default_value_when_not_provided():
+                result = execute_query(
+                    """
+                    query ($input: TestInputObject = {
+                      a: "foo", b: ["bar"], c: "baz"}) {
+                        fieldWithObjectInput(input: $input)
+                    }
+                    """
+                )
+
+                assert result == (
+                    {"fieldWithObjectInput": "{'a': 'foo', 'b': ['bar'], 'c': 'baz'}"},
+                    None,
+                )
+
+            def does_not_use_default_value_when_provided():
+                result = execute_query(
+                    """
+                    query q($input: String = "Default value") {
+                      fieldWithNullableStringInput(input: $input)
+                    }
+                    """,
+                    {"input": "Variable value"},
+                )
+
+                assert result == (
+                    {"fieldWithNullableStringInput": "'Variable value'"},
+                    None,
+                )
+
+            def uses_explicit_null_value_instead_of_default_value():
+                result = execute_query(
+                    """
+                    query q($input: String = "Default value") {
+                      fieldWithNullableStringInput(input: $input)
+                    }
+                    """,
+                    {"input": None},
+                )
+
+                assert result == ({"fieldWithNullableStringInput": "None"}, None)
+
+            def uses_null_default_value_when_not_provided():
+                result = execute_query(
+                    """
+                    query q($input: String = null) {
+                      fieldWithNullableStringInput(input: $input)
+                    }
+                    """,
+                    {},
+                )  # Intentionally missing variable values.
+
+                assert result == ({"fieldWithNullableStringInput": "None"}, None)
+
+            def properly_parses_single_value_to_list():
+                params = {"input": {"a": "foo", "b": "bar", "c": "baz"}}
+                result = execute_query(doc, params)
+
+                assert result == (
+                    {"fieldWithObjectInput": "{'a': 'foo', 'b': ['bar'], 'c': 'baz'}"},
+                    None,
+                )
+
+            def executes_with_complex_scalar_input():
+                params = {"input": {"c": "foo", "d": "SerializedValue"}}
+                result = execute_query(doc, params)
+
+                assert result == (
+                    {"fieldWithObjectInput": "{'c': 'foo', 'd': 'DeserializedValue'}"},
+                    None,
+                )
+
+            def errors_on_null_for_nested_non_null():
+                params = {"input": {"a": "foo", "b": "bar", "c": None}}
+                result = execute_query(doc, params)
+
+                assert result == (
+                    None,
+                    [
+                        {
+                            "message": "Variable '$input' got invalid value"
+                            " None at 'input.c';"
+                            " Expected non-nullable type 'String!' not to be None.",
+                            "locations": [(2, 24)],
+                        }
+                    ],
+                )
+
+            def errors_on_incorrect_type():
+                result = execute_query(doc, {"input": "foo bar"})
+
+                assert result == (
+                    None,
+                    [
+                        {
+                            "message": "Variable '$input' got invalid value 'foo bar';"
+                            " Expected type 'TestInputObject' to be a dict.",
+                            "locations": [(2, 24)],
+                            "path": None,
+                        }
+                    ],
+                )
+
+            def errors_on_omission_of_nested_non_null():
+                result = execute_query(doc, {"input": {"a": "foo", "b": "bar"}})
+
+                assert result == (
+                    None,
+                    [
+                        {
+                            "message": "Variable '$input' got invalid value"
+                            " {'a': 'foo', 'b': 'bar'};"
+                            " Field 'c' of required type 'String!' was not provided.",
+                            "locations": [(2, 24)],
+                        }
+                    ],
+                )
+
+            def errors_on_deep_nested_errors_and_with_many_errors():
+                nested_doc = """
+                    query ($input: TestNestedInputObject) {
+                      fieldWithNestedObjectInput(input: $input)
+                    }
+                    """
+                result = execute_query(nested_doc, {"input": {"na": {"a": "foo"}}})
+
+                assert result == (
+                    None,
+                    [
+                        {
+                            "message": "Variable '$input' got invalid value"
+                            " {'a': 'foo'} at 'input.na';"
+                            " Field 'c' of required type 'String!' was not provided.",
+                            "locations": [(2, 28)],
+                        },
+                        {
+                            "message": "Variable '$input' got invalid value"
+                            " {'na': {'a': 'foo'}};"
+                            " Field 'nb' of required type 'String!' was not provided.",
+                            "locations": [(2, 28)],
+                        },
+                    ],
+                )
+
+            def errors_on_addition_of_unknown_input_field():
+                params = {"input": {"a": "foo", "b": "bar", "c": "baz", "extra": "dog"}}
+                result = execute_query(doc, params)
+
+                assert result == (
+                    None,
+                    [
+                        {
+                            "message": "Variable '$input' got invalid value {'a':"
+                            " 'foo', 'b': 'bar', 'c': 'baz', 'extra': 'dog'}; Field"
+                            " 'extra' is not defined by type 'TestInputObject'.",
+                            "locations": [(2, 24)],
+                        }
+                    ],
+                )
+
+    def describe_handles_custom_enum_values():
+        def allows_custom_enum_values_as_inputs():
+            result = execute_query(
+                """
+                {
+                  null: fieldWithEnumInput(input: NULL)
+                  NaN: fieldWithEnumInput(input: NAN)
+                  false: fieldWithEnumInput(input: FALSE)
+                  customValue: fieldWithEnumInput(input: CUSTOM)
+                  defaultValue: fieldWithEnumInput(input: DEFAULT_VALUE)
+                }
+                """
+            )
+
+            assert result == (
+                {
+                    "null": "None",
+                    "NaN": "nan",
+                    "false": "False",
+                    "customValue": "'custom value'",
+                    # different from graphql.js, enum values are always wrapped
+                    "defaultValue": "None",
+                },
+                None,
+            )
+
+        def allows_non_nullable_inputs_to_have_null_as_enum_custom_value():
+            result = execute_query(
+                """
+                {
+                   fieldWithNonNullableEnumInput(input: NULL)
+                }
+                """
+            )
+
+            assert result == ({"fieldWithNonNullableEnumInput": "None"}, None)
+
+    def describe_handles_nullable_scalars():
+        def allows_nullable_inputs_to_be_omitted():
+            result = execute_query(
+                """
+                {
+                  fieldWithNullableStringInput
+                }
+                """
+            )
+
+            assert result == ({"fieldWithNullableStringInput": None}, None)
+
+        def allows_nullable_inputs_to_be_omitted_in_a_variable():
+            result = execute_query(
+                """
+                query ($value: String) {
+                  fieldWithNullableStringInput(input: $value)
+                }
+                """
+            )
+
+            assert result == ({"fieldWithNullableStringInput": None}, None)
+
+        def allows_nullable_inputs_to_be_omitted_in_an_unlisted_variable():
+            result = execute_query(
+                """
+                query SetsNullable {
+                  fieldWithNullableStringInput(input: $value)
+                }
+                """
+            )
+
+            assert result == ({"fieldWithNullableStringInput": None}, None)
+
+        def allows_nullable_inputs_to_be_set_to_null_in_a_variable():
+            doc = """
+                query SetsNullable($value: String) {
+                  fieldWithNullableStringInput(input: $value)
+                }
+                """
+            result = execute_query(doc, {"value": None})
+
+            assert result == ({"fieldWithNullableStringInput": "None"}, None)
+
+        def allows_nullable_inputs_to_be_set_to_a_value_in_a_variable():
+            doc = """
+                query SetsNullable($value: String) {
+                  fieldWithNullableStringInput(input: $value)
+                }
+                """
+            result = execute_query(doc, {"value": "a"})
+
+            assert result == ({"fieldWithNullableStringInput": "'a'"}, None)
+
+        def allows_nullable_inputs_to_be_set_to_a_value_directly():
+            result = execute_query(
+                """
+                {
+                  fieldWithNullableStringInput(input: "a")
+                }
+                """
+            )
+
+            assert result == ({"fieldWithNullableStringInput": "'a'"}, None)
+
+    def describe_handles_non_nullable_scalars():
+        def allows_non_nullable_variable_to_be_omitted_given_a_default():
+            result = execute_query(
+                """
+                query ($value: String! = "default") {
+                  fieldWithNullableStringInput(input: $value)
+                }
+                """
+            )
+
+            assert result == ({"fieldWithNullableStringInput": "'default'"}, None)
+
+        def allows_non_nullable_inputs_to_be_omitted_given_a_default():
+            result = execute_query(
+                """
+                query ($value: String = "default") {
+                  fieldWithNonNullableStringInput(input: $value)
+                }
+                """
+            )
+
+            assert result == ({"fieldWithNonNullableStringInput": "'default'"}, None)
+
+        def does_not_allow_non_nullable_inputs_to_be_omitted_in_a_variable():
+            result = execute_query(
+                """
+                query ($value: String!) {
+                  fieldWithNonNullableStringInput(input: $value)
+                }
+                """
+            )
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$value' of required type 'String!'"
+                        " was not provided.",
+                        "locations": [(2, 24)],
+                        "path": None,
+                    }
+                ],
+            )
+
+        def does_not_allow_non_nullable_inputs_to_be_set_to_null_in_variable():
+            doc = """
+                query ($value: String!) {
+                  fieldWithNonNullableStringInput(input: $value)
+                }
+                """
+            result = execute_query(doc, {"value": None})
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$value' of non-null type 'String!'"
+                        " must not be null.",
+                        "locations": [(2, 24)],
+                        "path": None,
+                    }
+                ],
+            )
+
+        def allows_non_nullable_inputs_to_be_set_to_a_value_in_a_variable():
+            doc = """
+                query ($value: String!) {
+                  fieldWithNonNullableStringInput(input: $value)
+                }
+                """
+            result = execute_query(doc, {"value": "a"})
+
+            assert result == ({"fieldWithNonNullableStringInput": "'a'"}, None)
+
+        def allows_non_nullable_inputs_to_be_set_to_a_value_directly():
+            result = execute_query(
+                """
+                {
+                  fieldWithNonNullableStringInput(input: "a")
+                }
+                """
+            )
+
+            assert result == ({"fieldWithNonNullableStringInput": "'a'"}, None)
+
+        def reports_error_for_missing_non_nullable_inputs():
+            result = execute_query("{ fieldWithNonNullableStringInput }")
+
+            assert result == (
+                {"fieldWithNonNullableStringInput": None},
+                [
+                    {
+                        "message": "Argument 'input' of required type 'String!'"
+                        " was not provided.",
+                        "locations": [(1, 3)],
+                        "path": ["fieldWithNonNullableStringInput"],
+                    }
+                ],
+            )
+
+        def reports_error_for_array_passed_into_string_input():
+            doc = """
+                query ($value: String!) {
+                  fieldWithNonNullableStringInput(input: $value)
+                }
+                """
+            result = execute_query(doc, {"value": [1, 2, 3]})
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$value' got invalid value [1, 2, 3];"
+                        " String cannot represent a non string value: [1, 2, 3]",
+                        "locations": [(2, 24)],
+                        "path": None,
+                    }
+                ],
+            )
+
+            assert result.errors[0].original_error is None
+
+        def reports_error_for_non_provided_variables_for_non_nullable_inputs():
+            # Note: this test would typically fail validation before
+            # encountering this execution error, however for queries which
+            # previously validated and are being run against a new schema which
+            # have introduced a breaking change to make a formerly non-required
+            # argument required, this asserts failure before allowing the
+            # underlying code to receive a non-null value.
+            result = execute_query(
+                """
+                {
+                  fieldWithNonNullableStringInput(input: $foo)
+                }
+                """
+            )
+
+            assert result == (
+                {"fieldWithNonNullableStringInput": None},
+                [
+                    {
+                        "message": "Argument 'input' of required type 'String!'"
+                        " was provided the variable '$foo' which was"
+                        " not provided a runtime value.",
+                        "locations": [(3, 58)],
+                        "path": ["fieldWithNonNullableStringInput"],
+                    }
+                ],
+            )
+
+    def describe_handles_lists_and_nullability():
+        def allows_lists_to_be_null():
+            doc = """
+                query ($input: [String]) {
+                  list(input: $input)
+                }
+                """
+            result = execute_query(doc, {"input": None})
+
+            assert result == ({"list": "None"}, None)
+
+        def allows_lists_to_contain_values():
+            doc = """
+                query ($input: [String]) {
+                  list(input: $input)
+                }
+                """
+            result = execute_query(doc, {"input": ["A"]})
+
+            assert result == ({"list": "['A']"}, None)
+
+        def allows_lists_to_contain_null():
+            doc = """
+                query ($input: [String]) {
+                  list(input: $input)
+                }
+                """
+
+            result = execute_query(doc, {"input": ["A", None, "B"]})
+
+            assert result == ({"list": "['A', None, 'B']"}, None)
+
+        def does_not_allow_non_null_lists_to_be_null():
+            doc = """
+                query ($input: [String]!) {
+                  nnList(input: $input)
+                }
+                """
+
+            result = execute_query(doc, {"input": None})
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$input' of non-null type '[String]!'"
+                        " must not be null.",
+                        "locations": [(2, 24)],
+                        "path": None,
+                    }
+                ],
+            )
+
+        def allows_non_null_lists_to_contain_values():
+            doc = """
+                query ($input: [String]!) {
+                  nnList(input: $input)
+                }
+                """
+
+            result = execute_query(doc, {"input": ["A"]})
+
+            assert result == ({"nnList": "['A']"}, None)
+
+        def allows_non_null_lists_to_contain_null():
+            doc = """
+                query ($input: [String]!) {
+                  nnList(input: $input)
+                }
+                """
+
+            result = execute_query(doc, {"input": ["A", None, "B"]})
+
+            assert result == ({"nnList": "['A', None, 'B']"}, None)
+
+        def allows_lists_of_non_nulls_to_be_null():
+            doc = """
+                query ($input: [String!]) {
+                  listNN(input: $input)
+                }
+                """
+
+            result = execute_query(doc, {"input": None})
+
+            assert result == ({"listNN": "None"}, None)
+
+        def allows_lists_of_non_nulls_to_contain_values():
+            doc = """
+                query ($input: [String!]) {
+                  listNN(input: $input)
+                }
+                """
+
+            result = execute_query(doc, {"input": ["A"]})
+
+            assert result == ({"listNN": "['A']"}, None)
+
+        def does_not_allow_lists_of_non_nulls_to_contain_null():
+            doc = """
+                query ($input: [String!]) {
+                  listNN(input: $input)
+                }
+                """
+            result = execute_query(doc, {"input": ["A", None, "B"]})
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$input' got invalid value None"
+                        " at 'input[1]';"
+                        " Expected non-nullable type 'String!' not to be None.",
+                        "locations": [(2, 24)],
+                    }
+                ],
+            )
+
+        def does_not_allow_non_null_lists_of_non_nulls_to_be_null():
+            doc = """
+                query ($input: [String!]!) {
+                  nnListNN(input: $input)
+                }
+                """
+            result = execute_query(doc, {"input": None})
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$input' of non-null type '[String!]!'"
+                        " must not be null.",
+                        "locations": [(2, 24)],
+                    }
+                ],
+            )
+
+        def allows_non_null_lists_of_non_nulls_to_contain_values():
+            doc = """
+                query ($input: [String!]!) {
+                  nnListNN(input: $input)
+                }
+                """
+            result = execute_query(doc, {"input": ["A"]})
+
+            assert result == ({"nnListNN": "['A']"}, None)
+
+        def does_not_allow_non_null_lists_of_non_nulls_to_contain_null():
+            doc = """
+                query ($input: [String!]!) {
+                  nnListNN(input: $input)
+                }
+                """
+            result = execute_query(doc, {"input": ["A", None, "B"]})
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$input' got invalid value None"
+                        " at 'input[1]';"
+                        " Expected non-nullable type 'String!' not to be None.",
+                        "locations": [(2, 24)],
+                        "path": None,
+                    }
+                ],
+            )
+
+        def does_not_allow_invalid_types_to_be_used_as_values():
+            doc = """
+                query ($input: TestType!) {
+                  fieldWithObjectInput(input: $input)
+                }
+                """
+            result = execute_query(doc, {"input": {"list": ["A", "B"]}})
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$input' expected value"
+                        " of type 'TestType!' which cannot"
+                        " be used as an input type.",
+                        "locations": [(2, 32)],
+                    }
+                ],
+            )
+
+        def does_not_allow_unknown_types_to_be_used_as_values():
+            doc = """
+                query ($input: UnknownType!) {
+                  fieldWithObjectInput(input: $input)
+                }
+                """
+            result = execute_query(doc, {"input": "whoKnows"})
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "Variable '$input' expected value"
+                        " of type 'UnknownType!' which cannot"
+                        " be used as an input type.",
+                        "locations": [(2, 32)],
+                    }
+                ],
+            )
+
+    def describe_execute_uses_argument_default_values():
+        def when_no_argument_provided():
+            result = execute_query("{ fieldWithDefaultArgumentValue }")
+
+            assert result == ({"fieldWithDefaultArgumentValue": "'Hello World'"}, None)
+
+        def when_omitted_variable_provided():
+            result = execute_query(
+                """
+                query ($optional: String) {
+                  fieldWithDefaultArgumentValue(input: $optional)
+                }
+                """
+            )
+
+            assert result == ({"fieldWithDefaultArgumentValue": "'Hello World'"}, None)
+
+        def not_when_argument_cannot_be_coerced():
+            result = execute_query(
+                """
+                {
+                  fieldWithDefaultArgumentValue(input: WRONG_TYPE)
+                }
+                """
+            )
+
+            assert result == (
+                {"fieldWithDefaultArgumentValue": None},
+                [
+                    {
+                        "message": "Argument 'input' has invalid value WRONG_TYPE.",
+                        "locations": [(3, 56)],
+                        "path": ["fieldWithDefaultArgumentValue"],
+                    }
+                ],
+            )
+
+        def when_no_runtime_value_is_provided_to_a_non_null_argument():
+            result = execute_query(
+                """
+                query optionalVariable($optional: String) {
+                  fieldWithNonNullableStringInputAndDefaultArgValue(input: $optional)
+                }
+                """
+            )
+
+            assert result == (
+                {"fieldWithNonNullableStringInputAndDefaultArgValue": "'Hello World'"},
+                None,
+            )
+
+    def describe_get_variable_values_limit_maximum_number_of_coercion_errors():
+        doc = parse(
+            """
+            query ($input: [String!]) {
+              listNN(input: $input)
+            }
+            """
+        )
+
+        operation = doc.definitions[0]
+        assert isinstance(operation, OperationDefinitionNode)
+        variable_definitions = operation.variable_definitions
+        assert variable_definitions is not None
+
+        input_value = {"input": [0, 1, 2]}
+
+        def _invalid_value_error(value, index):
+            return {
+                "message": "Variable '$input' got invalid value"
+                f" {value} at 'input[{index}]';"
+                f" String cannot represent a non string value: {value}",
+                "locations": [(2, 20)],
+            }
+
+        def return_all_errors_by_default():
+            result = get_variable_values(schema, variable_definitions, input_value)
+
+            assert result == [
+                _invalid_value_error(0, 0),
+                _invalid_value_error(1, 1),
+                _invalid_value_error(2, 2),
+            ]
+
+        def when_max_errors_is_equal_to_number_of_errors():
+            result = get_variable_values(
+                schema, variable_definitions, input_value, max_errors=3
+            )
+
+            assert result == [
+                _invalid_value_error(0, 0),
+                _invalid_value_error(1, 1),
+                _invalid_value_error(2, 2),
+            ]
+
+        def when_max_errors_is_less_than_number_of_errors():
+            result = get_variable_values(
+                schema, variable_definitions, input_value, max_errors=2
+            )
+
+            assert result == [
+                _invalid_value_error(0, 0),
+                _invalid_value_error(1, 1),
+                {
+                    "message": "Too many errors processing variables,"
+                    " error limit reached. Execution aborted."
+                },
+            ]
diff --git a/tests/fixtures/__init__.py b/tests/fixtures/__init__.py
new file mode 100644
index 0000000..430622f
--- /dev/null
+++ b/tests/fixtures/__init__.py
@@ -0,0 +1,42 @@
+"""Fixtures for graphql tests"""
+import json
+from os.path import dirname, join
+
+from pytest import fixture  # type: ignore
+
+__all__ = [
+    "kitchen_sink_query",
+    "kitchen_sink_sdl",
+    "big_schema_sdl",
+    "big_schema_introspection_result",
+]
+
+
+def read_graphql(name):
+    path = join(dirname(__file__), name + ".graphql")
+    return open(path, encoding="utf-8").read()
+
+
+def read_json(name):
+    path = join(dirname(__file__), name + ".json")
+    return json.load(open(path, encoding="utf-8"))
+
+
+@fixture(scope="module")
+def kitchen_sink_query():
+    return read_graphql("kitchen_sink")
+
+
+@fixture(scope="module")
+def kitchen_sink_sdl():
+    return read_graphql("schema_kitchen_sink")
+
+
+@fixture(scope="module")
+def big_schema_sdl():
+    return read_graphql("github_schema")
+
+
+@fixture(scope="module")
+def big_schema_introspection_result():
+    return read_json("github_schema")
diff --git a/tests/fixtures/github_schema.graphql b/tests/fixtures/github_schema.graphql
new file mode 100644
index 0000000..8446dd0
--- /dev/null
+++ b/tests/fixtures/github_schema.graphql
@@ -0,0 +1,13158 @@
+"""Autogenerated input type of AcceptTopicSuggestion"""
+input AcceptTopicSuggestionInput {
+  """The Node ID of the repository."""
+  repositoryId: ID!
+
+  """The name of the suggested topic."""
+  name: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AcceptTopicSuggestion"""
+type AcceptTopicSuggestionPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The accepted topic."""
+  topic: Topic
+}
+
+"""
+Represents an object which can take actions on GitHub. Typically a User or Bot.
+"""
+interface Actor {
+  """A URL pointing to the actor's public avatar."""
+  avatarUrl(
+    """The size of the resulting square image."""
+    size: Int
+  ): URI!
+
+  """The username of the actor."""
+  login: String!
+
+  """The HTTP path for this actor."""
+  resourcePath: URI!
+
+  """The HTTP URL for this actor."""
+  url: URI!
+}
+
+"""Autogenerated input type of AddAssigneesToAssignable"""
+input AddAssigneesToAssignableInput {
+  """The id of the assignable object to add assignees to."""
+  assignableId: ID!
+
+  """The id of users to add as assignees."""
+  assigneeIds: [ID!]!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddAssigneesToAssignable"""
+type AddAssigneesToAssignablePayload {
+  """The item that was assigned."""
+  assignable: Assignable
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated input type of AddComment"""
+input AddCommentInput {
+  """The Node ID of the subject to modify."""
+  subjectId: ID!
+
+  """The contents of the comment."""
+  body: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddComment"""
+type AddCommentPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The edge from the subject's comment connection."""
+  commentEdge: IssueCommentEdge
+
+  """The subject"""
+  subject: Node
+
+  """The edge from the subject's timeline connection."""
+  timelineEdge: IssueTimelineItemEdge
+}
+
+"""
+Represents a 'added_to_project' event on a given issue or pull request.
+"""
+type AddedToProjectEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+}
+
+"""Autogenerated input type of AddLabelsToLabelable"""
+input AddLabelsToLabelableInput {
+  """The id of the labelable object to add labels to."""
+  labelableId: ID!
+
+  """The ids of the labels to add."""
+  labelIds: [ID!]!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddLabelsToLabelable"""
+type AddLabelsToLabelablePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The item that was labeled."""
+  labelable: Labelable
+}
+
+"""Autogenerated input type of AddProjectCard"""
+input AddProjectCardInput {
+  """The Node ID of the ProjectColumn."""
+  projectColumnId: ID!
+
+  """The content of the card. Must be a member of the ProjectCardItem union"""
+  contentId: ID
+
+  """The note on the card."""
+  note: String
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddProjectCard"""
+type AddProjectCardPayload {
+  """The edge from the ProjectColumn's card connection."""
+  cardEdge: ProjectCardEdge
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The ProjectColumn"""
+  projectColumn: ProjectColumn
+}
+
+"""Autogenerated input type of AddProjectColumn"""
+input AddProjectColumnInput {
+  """The Node ID of the project."""
+  projectId: ID!
+
+  """The name of the column."""
+  name: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddProjectColumn"""
+type AddProjectColumnPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The edge from the project's column connection."""
+  columnEdge: ProjectColumnEdge
+
+  """The project"""
+  project: Project
+}
+
+"""Autogenerated input type of AddPullRequestReviewComment"""
+input AddPullRequestReviewCommentInput {
+  """The Node ID of the review to modify."""
+  pullRequestReviewId: ID!
+
+  """The SHA of the commit to comment on."""
+  commitOID: GitObjectID
+
+  """The text of the comment."""
+  body: String!
+
+  """The relative path of the file to comment on."""
+  path: String
+
+  """The line index in the diff to comment on."""
+  position: Int
+
+  """The comment id to reply to."""
+  inReplyTo: ID
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddPullRequestReviewComment"""
+type AddPullRequestReviewCommentPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The newly created comment."""
+  comment: PullRequestReviewComment
+
+  """The edge from the review's comment connection."""
+  commentEdge: PullRequestReviewCommentEdge
+}
+
+"""Autogenerated input type of AddPullRequestReview"""
+input AddPullRequestReviewInput {
+  """The Node ID of the pull request to modify."""
+  pullRequestId: ID!
+
+  """The commit OID the review pertains to."""
+  commitOID: GitObjectID
+
+  """The contents of the review body comment."""
+  body: String
+
+  """The event to perform on the pull request review."""
+  event: PullRequestReviewEvent
+
+  """The review line comments."""
+  comments: [DraftPullRequestReviewComment]
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddPullRequestReview"""
+type AddPullRequestReviewPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The newly created pull request review."""
+  pullRequestReview: PullRequestReview
+
+  """The edge from the pull request's review connection."""
+  reviewEdge: PullRequestReviewEdge
+}
+
+"""Autogenerated input type of AddReaction"""
+input AddReactionInput {
+  """The Node ID of the subject to modify."""
+  subjectId: ID!
+
+  """The name of the emoji to react with."""
+  content: ReactionContent!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddReaction"""
+type AddReactionPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The reaction object."""
+  reaction: Reaction
+
+  """The reactable subject."""
+  subject: Reactable
+}
+
+"""Autogenerated input type of AddStar"""
+input AddStarInput {
+  """The Starrable ID to star."""
+  starrableId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of AddStar"""
+type AddStarPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The starrable."""
+  starrable: Starrable
+}
+
+"""A GitHub App."""
+type App implements Node {
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The description of the app."""
+  description: String
+  id: ID!
+
+  """The hex color code, without the leading '#', for the logo background."""
+  logoBackgroundColor: String!
+
+  """A URL pointing to the app's logo."""
+  logoUrl(
+    """The size of the resulting image."""
+    size: Int
+  ): URI!
+
+  """The name of the app."""
+  name: String!
+
+  """A slug based on the name of the app for use in URLs."""
+  slug: String!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The URL to the app's homepage."""
+  url: URI!
+}
+
+"""An edge in a connection."""
+type AppEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: App
+}
+
+"""An object that can have users assigned to it."""
+interface Assignable {
+  """A list of Users assigned to this object."""
+  assignees(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+}
+
+"""Represents an 'assigned' event on any assignable object."""
+type AssignedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the assignable associated with the event."""
+  assignable: Assignable!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Identifies the user who was assigned."""
+  user: User
+}
+
+"""
+Represents a 'base_ref_changed' event on a given issue or pull request.
+"""
+type BaseRefChangedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+}
+
+"""Represents a 'base_ref_force_pushed' event on a given pull request."""
+type BaseRefForcePushedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the after commit SHA for the 'base_ref_force_pushed' event."""
+  afterCommit: Commit
+
+  """
+  Identifies the before commit SHA for the 'base_ref_force_pushed' event.
+  """
+  beforeCommit: Commit
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+
+  """
+  Identifies the fully qualified ref name for the 'base_ref_force_pushed' event.
+  """
+  ref: Ref
+}
+
+"""Represents a Git blame."""
+type Blame {
+  """The list of ranges from a Git blame."""
+  ranges: [BlameRange!]!
+}
+
+"""Represents a range of information from a Git blame."""
+type BlameRange {
+  """
+  Identifies the recency of the change, from 1 (new) to 10 (old). This is
+  calculated as a 2-quantile and determines the length of distance between the
+  median age of all the changes in the file and the recency of the current
+  range's change.
+  """
+  age: Int!
+
+  """Identifies the line author"""
+  commit: Commit!
+
+  """The ending line for the range"""
+  endingLine: Int!
+
+  """The starting line for the range"""
+  startingLine: Int!
+}
+
+"""Represents a Git blob."""
+type Blob implements Node & GitObject {
+  """An abbreviated version of the Git object ID"""
+  abbreviatedOid: String!
+
+  """Byte size of Blob object"""
+  byteSize: Int!
+
+  """The HTTP path for this Git object"""
+  commitResourcePath: URI!
+
+  """The HTTP URL for this Git object"""
+  commitUrl: URI!
+  id: ID!
+
+  """Indicates whether the Blob is binary or text"""
+  isBinary: Boolean!
+
+  """Indicates whether the contents is truncated"""
+  isTruncated: Boolean!
+
+  """The Git object ID"""
+  oid: GitObjectID!
+
+  """The Repository the Git object belongs to"""
+  repository: Repository!
+
+  """UTF8 text data or null if the Blob is binary"""
+  text: String
+}
+
+"""A special type of user which takes actions on behalf of GitHub Apps."""
+type Bot implements Node & Actor & UniformResourceLocatable {
+  """A URL pointing to the GitHub App's public avatar."""
+  avatarUrl(
+    """The size of the resulting square image."""
+    size: Int
+  ): URI!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+
+  """The username of the actor."""
+  login: String!
+
+  """The HTTP path for this bot"""
+  resourcePath: URI!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this bot"""
+  url: URI!
+}
+
+"""A branch protection rule."""
+type BranchProtectionRule implements Node {
+  """
+  A list of conflicts matching branches protection rule and other branch protection rules
+  """
+  branchProtectionRuleConflicts(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): BranchProtectionRuleConflictConnection!
+
+  """The actor who created this branch protection rule."""
+  creator: Actor
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """
+  Will new commits pushed to matching branches dismiss pull request review approvals.
+  """
+  dismissesStaleReviews: Boolean!
+  id: ID!
+
+  """Can admins overwrite branch protection."""
+  isAdminEnforced: Boolean!
+
+  """Repository refs that are protected by this rule"""
+  matchingRefs(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RefConnection!
+
+  """Identifies the protection rule pattern."""
+  pattern: String!
+
+  """A list push allowances for this branch protection rule."""
+  pushAllowances(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PushAllowanceConnection!
+
+  """The repository associated with this branch protection rule."""
+  repository: Repository
+
+  """Number of approving reviews required to update matching branches."""
+  requiredApprovingReviewCount: Int
+
+  """
+  List of required status check contexts that must pass for commits to be accepted to matching branches.
+  """
+  requiredStatusCheckContexts: [String]
+
+  """Are approving reviews required to update matching branches."""
+  requiresApprovingReviews: Boolean!
+
+  """Are commits required to be signed."""
+  requiresCommitSignatures: Boolean!
+
+  """Are status checks required to update matching branches."""
+  requiresStatusChecks: Boolean!
+
+  """Are branches required to be up to date before merging."""
+  requiresStrictStatusChecks: Boolean!
+
+  """Is pushing to matching branches restricted."""
+  restrictsPushes: Boolean!
+
+  """Is dismissal of pull request reviews restricted."""
+  restrictsReviewDismissals: Boolean!
+
+  """A list review dismissal allowances for this branch protection rule."""
+  reviewDismissalAllowances(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ReviewDismissalAllowanceConnection!
+}
+
+"""A conflict between two branch protection rules."""
+type BranchProtectionRuleConflict {
+  """Identifies the branch protection rule."""
+  branchProtectionRule: BranchProtectionRule
+
+  """Identifies the conflicting branch protection rule."""
+  conflictingBranchProtectionRule: BranchProtectionRule
+
+  """Identifies the branch ref that has conflicting rules"""
+  ref: Ref
+}
+
+"""The connection type for BranchProtectionRuleConflict."""
+type BranchProtectionRuleConflictConnection {
+  """A list of edges."""
+  edges: [BranchProtectionRuleConflictEdge]
+
+  """A list of nodes."""
+  nodes: [BranchProtectionRuleConflict]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type BranchProtectionRuleConflictEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: BranchProtectionRuleConflict
+}
+
+"""The connection type for BranchProtectionRule."""
+type BranchProtectionRuleConnection {
+  """A list of edges."""
+  edges: [BranchProtectionRuleEdge]
+
+  """A list of nodes."""
+  nodes: [BranchProtectionRule]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type BranchProtectionRuleEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: BranchProtectionRule
+}
+
+"""Autogenerated input type of ChangeUserStatus"""
+input ChangeUserStatusInput {
+  """
+  The emoji to represent your status. Can either be a native Unicode emoji or an emoji name with colons, e.g., :grinning:.
+  """
+  emoji: String
+
+  """A short description of your current status."""
+  message: String
+
+  """
+  The ID of the organization whose members will be allowed to see the status. If
+  omitted, the status will be publicly visible.
+  """
+  organizationId: ID
+
+  """
+  Whether this status should indicate you are not fully available on GitHub, e.g., you are away.
+  """
+  limitedAvailability: Boolean = false
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of ChangeUserStatus"""
+type ChangeUserStatusPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """Your updated status."""
+  status: UserStatus
+}
+
+"""Autogenerated input type of ClearLabelsFromLabelable"""
+input ClearLabelsFromLabelableInput {
+  """The id of the labelable object to clear the labels from."""
+  labelableId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of ClearLabelsFromLabelable"""
+type ClearLabelsFromLabelablePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The item that was unlabeled."""
+  labelable: Labelable
+}
+
+"""Autogenerated input type of CloneProject"""
+input CloneProjectInput {
+  """The owner ID to create the project under."""
+  targetOwnerId: ID!
+
+  """The source project to clone."""
+  sourceId: ID!
+
+  """Whether or not to clone the source project's workflows."""
+  includeWorkflows: Boolean!
+
+  """The name of the project."""
+  name: String!
+
+  """The description of the project."""
+  body: String
+
+  """The visibility of the project, defaults to false (private)."""
+  public: Boolean
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of CloneProject"""
+type CloneProjectPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The id of the JobStatus for populating cloned fields."""
+  jobStatusId: String
+
+  """The new cloned project."""
+  project: Project
+}
+
+"""An object that can be closed"""
+interface Closable {
+  """
+  `true` if the object is closed (definition of closed may depend on type)
+  """
+  closed: Boolean!
+
+  """Identifies the date and time when the object was closed."""
+  closedAt: DateTime
+}
+
+"""Represents a 'closed' event on any `Closable`."""
+type ClosedEvent implements Node & UniformResourceLocatable {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Object that was closed."""
+  closable: Closable!
+
+  """Object which triggered the creation of this event."""
+  closer: Closer
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """The HTTP path for this closed event."""
+  resourcePath: URI!
+
+  """The HTTP URL for this closed event."""
+  url: URI!
+}
+
+"""Autogenerated input type of CloseIssue"""
+input CloseIssueInput {
+  """ID of the issue to be closed."""
+  issueId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of CloseIssue"""
+type CloseIssuePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The issue that was closed."""
+  issue: Issue
+}
+
+"""Autogenerated input type of ClosePullRequest"""
+input ClosePullRequestInput {
+  """ID of the pull request to be closed."""
+  pullRequestId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of ClosePullRequest"""
+type ClosePullRequestPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The pull request that was closed."""
+  pullRequest: PullRequest
+}
+
+"""The object which triggered a `ClosedEvent`."""
+union Closer = Commit | PullRequest
+
+"""The Code of Conduct for a repository"""
+type CodeOfConduct implements Node {
+  """The body of the Code of Conduct"""
+  body: String
+  id: ID!
+
+  """The key for the Code of Conduct"""
+  key: String!
+
+  """The formal name of the Code of Conduct"""
+  name: String!
+
+  """The HTTP path for this Code of Conduct"""
+  resourcePath: URI
+
+  """The HTTP URL for this Code of Conduct"""
+  url: URI
+}
+
+"""Collaborators affiliation level with a subject."""
+enum CollaboratorAffiliation {
+  """All outside collaborators of an organization-owned subject."""
+  OUTSIDE
+
+  """
+  All collaborators with permissions to an organization-owned subject, regardless of organization membership status.
+  """
+  DIRECT
+
+  """All collaborators the authenticated user can see."""
+  ALL
+}
+
+"""Types that can be inside Collection Items."""
+union CollectionItemContent = Repository | Organization | User
+
+"""Represents a comment."""
+interface Comment {
+  """The actor who authored the comment."""
+  author: Actor
+
+  """Author's association with the subject of the comment."""
+  authorAssociation: CommentAuthorAssociation!
+
+  """The body as Markdown."""
+  body: String!
+
+  """The body rendered to HTML."""
+  bodyHTML: HTML!
+
+  """The body rendered to text."""
+  bodyText: String!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Check if this comment was created via an email reply."""
+  createdViaEmail: Boolean!
+
+  """The actor who edited the comment."""
+  editor: Actor
+  id: ID!
+
+  """
+  Check if this comment was edited and includes an edit with the creation data
+  """
+  includesCreatedEdit: Boolean!
+
+  """The moment the editor made the last edit"""
+  lastEditedAt: DateTime
+
+  """Identifies when the comment was published at."""
+  publishedAt: DateTime
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """A list of edits to this content."""
+  userContentEdits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserContentEditConnection
+
+  """Did the viewer author this comment."""
+  viewerDidAuthor: Boolean!
+}
+
+"""A comment author association with repository."""
+enum CommentAuthorAssociation {
+  """Author is a member of the organization that owns the repository."""
+  MEMBER
+
+  """Author is the owner of the repository."""
+  OWNER
+
+  """Author has been invited to collaborate on the repository."""
+  COLLABORATOR
+
+  """Author has previously committed to the repository."""
+  CONTRIBUTOR
+
+  """Author has not previously committed to the repository."""
+  FIRST_TIME_CONTRIBUTOR
+
+  """Author has not previously committed to GitHub."""
+  FIRST_TIMER
+
+  """Author has no association with the repository."""
+  NONE
+}
+
+"""The possible errors that will prevent a user from updating a comment."""
+enum CommentCannotUpdateReason {
+  """
+  You must be the author or have write access to this repository to update this comment.
+  """
+  INSUFFICIENT_ACCESS
+
+  """Unable to create comment because issue is locked."""
+  LOCKED
+
+  """You must be logged in to update this comment."""
+  LOGIN_REQUIRED
+
+  """Repository is under maintenance."""
+  MAINTENANCE
+
+  """At least one email address must be verified to update this comment."""
+  VERIFIED_EMAIL_REQUIRED
+
+  """You cannot update this comment"""
+  DENIED
+}
+
+"""Represents a 'comment_deleted' event on a given issue or pull request."""
+type CommentDeletedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+}
+
+"""Represents a Git commit."""
+type Commit implements Node & GitObject & Subscribable & UniformResourceLocatable {
+  """An abbreviated version of the Git object ID"""
+  abbreviatedOid: String!
+
+  """The number of additions in this commit."""
+  additions: Int!
+
+  """The pull requests associated with a commit"""
+  associatedPullRequests(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Ordering options for pull requests."""
+    orderBy: PullRequestOrder
+  ): PullRequestConnection
+
+  """Authorship details of the commit."""
+  author: GitActor
+
+  """Check if the committer and the author match."""
+  authoredByCommitter: Boolean!
+
+  """The datetime when this commit was authored."""
+  authoredDate: DateTime!
+
+  """Fetches `git blame` information."""
+  blame(
+    """The file whose Git blame information you want."""
+    path: String!
+  ): Blame!
+
+  """The number of changed files in this commit."""
+  changedFiles: Int!
+
+  """Comments made on the commit."""
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): CommitCommentConnection!
+
+  """The HTTP path for this Git object"""
+  commitResourcePath: URI!
+
+  """The HTTP URL for this Git object"""
+  commitUrl: URI!
+
+  """The datetime when this commit was committed."""
+  committedDate: DateTime!
+
+  """Check if commited via GitHub web UI."""
+  committedViaWeb: Boolean!
+
+  """Committership details of the commit."""
+  committer: GitActor
+
+  """The number of deletions in this commit."""
+  deletions: Int!
+
+  """The deployments associated with a commit."""
+  deployments(
+    """Environments to list deployments for"""
+    environments: [String!]
+
+    """Ordering options for deployments returned from the connection."""
+    orderBy: DeploymentOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): DeploymentConnection
+
+  """
+  The linear commit history starting from (and including) this commit, in the same order as `git log`.
+  """
+  history(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """
+    If non-null, filters history to only show commits touching files under this path.
+    """
+    path: String
+
+    """
+    If non-null, filters history to only show commits with matching authorship.
+    """
+    author: CommitAuthor
+
+    """Allows specifying a beginning time or date for fetching commits."""
+    since: GitTimestamp
+
+    """Allows specifying an ending time or date for fetching commits."""
+    until: GitTimestamp
+  ): CommitHistoryConnection!
+  id: ID!
+
+  """The Git commit message"""
+  message: String!
+
+  """The Git commit message body"""
+  messageBody: String!
+
+  """The commit message body rendered to HTML."""
+  messageBodyHTML: HTML!
+
+  """The Git commit message headline"""
+  messageHeadline: String!
+
+  """The commit message headline rendered to HTML."""
+  messageHeadlineHTML: HTML!
+
+  """The Git object ID"""
+  oid: GitObjectID!
+
+  """The parents of a commit."""
+  parents(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): CommitConnection!
+
+  """The datetime when this commit was pushed."""
+  pushedDate: DateTime
+
+  """The Repository this commit belongs to"""
+  repository: Repository!
+
+  """The HTTP path for this commit"""
+  resourcePath: URI!
+
+  """Commit signing information, if present."""
+  signature: GitSignature
+
+  """Status information for this commit"""
+  status: Status
+
+  """
+  Returns a URL to download a tarball archive for a repository.
+  Note: For private repositories, these links are temporary and expire after five minutes.
+  """
+  tarballUrl: URI!
+
+  """Commit's root Tree"""
+  tree: Tree!
+
+  """The HTTP path for the tree of this commit"""
+  treeResourcePath: URI!
+
+  """The HTTP URL for the tree of this commit"""
+  treeUrl: URI!
+
+  """The HTTP URL for this commit"""
+  url: URI!
+
+  """
+  Check if the viewer is able to change their subscription status for the repository.
+  """
+  viewerCanSubscribe: Boolean!
+
+  """
+  Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+  """
+  viewerSubscription: SubscriptionState
+
+  """
+  Returns a URL to download a zipball archive for a repository.
+  Note: For private repositories, these links are temporary and expire after five minutes.
+  """
+  zipballUrl: URI!
+}
+
+"""Specifies an author for filtering Git commits."""
+input CommitAuthor {
+  """
+  ID of a User to filter by. If non-null, only commits authored by this user
+  will be returned. This field takes precedence over emails.
+  """
+  id: ID
+
+  """
+  Email addresses to filter by. Commits authored by any of the specified email addresses will be returned.
+  """
+  emails: [String!]
+}
+
+"""Represents a comment on a given Commit."""
+type CommitComment implements Node & Comment & Deletable & Updatable & UpdatableComment & Reactable & RepositoryNode {
+  """The actor who authored the comment."""
+  author: Actor
+
+  """Author's association with the subject of the comment."""
+  authorAssociation: CommentAuthorAssociation!
+
+  """Identifies the comment body."""
+  body: String!
+
+  """Identifies the comment body rendered to HTML."""
+  bodyHTML: HTML!
+
+  """The body rendered to text."""
+  bodyText: String!
+
+  """
+  Identifies the commit associated with the comment, if the commit exists.
+  """
+  commit: Commit
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Check if this comment was created via an email reply."""
+  createdViaEmail: Boolean!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The actor who edited the comment."""
+  editor: Actor
+  id: ID!
+
+  """
+  Check if this comment was edited and includes an edit with the creation data
+  """
+  includesCreatedEdit: Boolean!
+
+  """Returns whether or not a comment has been minimized."""
+  isMinimized: Boolean!
+
+  """The moment the editor made the last edit"""
+  lastEditedAt: DateTime
+
+  """Returns why the comment was minimized."""
+  minimizedReason: String
+
+  """Identifies the file path associated with the comment."""
+  path: String
+
+  """Identifies the line position associated with the comment."""
+  position: Int
+
+  """Identifies when the comment was published at."""
+  publishedAt: DateTime
+
+  """A list of reactions grouped by content left on the subject."""
+  reactionGroups: [ReactionGroup!]
+
+  """A list of Reactions left on the Issue."""
+  reactions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Allows filtering Reactions by emoji."""
+    content: ReactionContent
+
+    """Allows specifying the order in which reactions are returned."""
+    orderBy: ReactionOrder
+  ): ReactionConnection!
+
+  """The repository associated with this node."""
+  repository: Repository!
+
+  """The HTTP path permalink for this commit comment."""
+  resourcePath: URI!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL permalink for this commit comment."""
+  url: URI!
+
+  """A list of edits to this content."""
+  userContentEdits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserContentEditConnection
+
+  """Check if the current viewer can delete this object."""
+  viewerCanDelete: Boolean!
+
+  """Check if the current viewer can minimize this object."""
+  viewerCanMinimize: Boolean!
+
+  """Can user react to this subject"""
+  viewerCanReact: Boolean!
+
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+
+  """Reasons why the current viewer can not update this comment."""
+  viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+  """Did the viewer author this comment."""
+  viewerDidAuthor: Boolean!
+}
+
+"""The connection type for CommitComment."""
+type CommitCommentConnection {
+  """A list of edges."""
+  edges: [CommitCommentEdge]
+
+  """A list of nodes."""
+  nodes: [CommitComment]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type CommitCommentEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: CommitComment
+}
+
+"""A thread of comments on a commit."""
+type CommitCommentThread implements Node & RepositoryNode {
+  """The comments that exist in this thread."""
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): CommitCommentConnection!
+
+  """The commit the comments were made on."""
+  commit: Commit!
+  id: ID!
+
+  """The file the comments were made on."""
+  path: String
+
+  """The position in the diff for the commit that the comment was made on."""
+  position: Int
+
+  """The repository associated with this node."""
+  repository: Repository!
+}
+
+"""The connection type for Commit."""
+type CommitConnection {
+  """A list of edges."""
+  edges: [CommitEdge]
+
+  """A list of nodes."""
+  nodes: [Commit]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Ordering options for commit contribution connections."""
+input CommitContributionOrder {
+  """The field by which to order commit contributions."""
+  field: CommitContributionOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which commit contribution connections can be ordered."""
+enum CommitContributionOrderField {
+  """Order commit contributions by when they were made."""
+  OCCURRED_AT
+
+  """Order commit contributions by how many commits they represent."""
+  COMMIT_COUNT
+}
+
+"""This aggregates commits made by a user within one repository."""
+type CommitContributionsByRepository {
+  """The commit contributions, each representing a day."""
+  contributions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """
+    Ordering options for commit contributions returned from the connection.
+    """
+    orderBy: CommitContributionOrder
+  ): CreatedCommitContributionConnection!
+
+  """The repository in which the commits were made."""
+  repository: Repository!
+
+  """
+  The HTTP path for the user's commits to the repository in this time range.
+  """
+  resourcePath: URI!
+
+  """
+  The HTTP URL for the user's commits to the repository in this time range.
+  """
+  url: URI!
+}
+
+"""An edge in a connection."""
+type CommitEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Commit
+}
+
+"""The connection type for Commit."""
+type CommitHistoryConnection {
+  """A list of edges."""
+  edges: [CommitEdge]
+
+  """A list of nodes."""
+  nodes: [Commit]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""A content attachment"""
+type ContentAttachment {
+  """
+  The body text of the content attachment. This parameter supports markdown.
+  """
+  body: String!
+
+  """The content reference that the content attachment is attached to."""
+  contentReference: ContentReference!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int!
+  id: ID!
+
+  """The title of the content attachment."""
+  title: String!
+}
+
+"""A content reference"""
+type ContentReference {
+  """Identifies the primary key from the database."""
+  databaseId: Int!
+  id: ID!
+
+  """The reference of the content reference."""
+  reference: String!
+}
+
+"""
+Represents a contribution a user made on GitHub, such as opening an issue.
+"""
+interface Contribution {
+  """
+  Whether this contribution is associated with a record you do not have access to. For
+  example, your own 'first issue' contribution may have been made on a repository you can no
+  longer access.
+
+  """
+  isRestricted: Boolean!
+
+  """When this contribution was made."""
+  occurredAt: DateTime!
+
+  """The HTTP path for this contribution."""
+  resourcePath: URI!
+
+  """The HTTP URL for this contribution."""
+  url: URI!
+
+  """
+  The user who made this contribution.
+
+  """
+  user: User!
+}
+
+"""A calendar of contributions made on GitHub by a user."""
+type ContributionCalendar {
+  """
+  A list of hex color codes used in this calendar. The darker the color, the more contributions it represents.
+  """
+  colors: [String!]!
+
+  """
+  Determine if the color set was chosen because it's currently Halloween.
+  """
+  isHalloween: Boolean!
+
+  """A list of the months of contributions in this calendar."""
+  months: [ContributionCalendarMonth!]!
+
+  """The count of total contributions in the calendar."""
+  totalContributions: Int!
+
+  """A list of the weeks of contributions in this calendar."""
+  weeks: [ContributionCalendarWeek!]!
+}
+
+"""Represents a single day of contributions on GitHub by a user."""
+type ContributionCalendarDay {
+  """
+  The hex color code that represents how many contributions were made on this day compared to others in the calendar.
+  """
+  color: String!
+
+  """How many contributions were made by the user on this day."""
+  contributionCount: Int!
+
+  """The day this square represents."""
+  date: Date!
+
+  """
+  A number representing which day of the week this square represents, e.g., 1 is Monday.
+  """
+  weekday: Int!
+}
+
+"""A month of contributions in a user's contribution graph."""
+type ContributionCalendarMonth {
+  """The date of the first day of this month."""
+  firstDay: Date!
+
+  """The name of the month."""
+  name: String!
+
+  """How many weeks started in this month."""
+  totalWeeks: Int!
+
+  """The year the month occurred in."""
+  year: Int!
+}
+
+"""A week of contributions in a user's contribution graph."""
+type ContributionCalendarWeek {
+  """The days of contributions in this week."""
+  contributionDays: [ContributionCalendarDay!]!
+
+  """The date of the earliest square in this week."""
+  firstDay: Date!
+}
+
+"""Ordering options for contribution connections."""
+input ContributionOrder {
+  """The field by which to order contributions."""
+  field: ContributionOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which contribution connections can be ordered."""
+enum ContributionOrderField {
+  """Order contributions by when they were made."""
+  OCCURRED_AT
+}
+
+"""
+A contributions collection aggregates contributions such as opened issues and commits created by a user.
+"""
+type ContributionsCollection {
+  """Commit contributions made by the user, grouped by repository."""
+  commitContributionsByRepository(
+    """How many repositories should be included."""
+    maxRepositories: Int = 25
+  ): [CommitContributionsByRepository!]!
+
+  """A calendar of this user's contributions on GitHub."""
+  contributionCalendar: ContributionCalendar!
+
+  """
+  The years the user has been making contributions with the most recent year first.
+  """
+  contributionYears: [Int!]!
+
+  """
+  Determine if this collection's time span ends in the current month.
+
+  """
+  doesEndInCurrentMonth: Boolean!
+
+  """
+  The date of the first restricted contribution the user made in this time
+  period. Can only be non-null when the user has enabled private contribution counts.
+  """
+  earliestRestrictedContributionDate: Date
+
+  """The ending date and time of this collection."""
+  endedAt: DateTime!
+
+  """
+  The first issue the user opened on GitHub. This will be null if that issue was
+  opened outside the collection's time range and ignoreTimeRange is false. If
+  the issue is not visible but the user has opted to show private contributions,
+  a RestrictedContribution will be returned.
+  """
+  firstIssueContribution(
+    """
+    If true, the first issue will be returned even if it was opened outside of the collection's time range.
+
+    **Upcoming Change on 2019-07-01 UTC**
+    **Description:** `ignoreTimeRange` will be removed. Use a `ContributionsCollection` starting sufficiently far back
+    **Reason:** ignore_time_range will be removed
+
+    """
+    ignoreTimeRange: Boolean = false
+  ): CreatedIssueOrRestrictedContribution
+
+  """
+  The first pull request the user opened on GitHub. This will be null if that
+  pull request was opened outside the collection's time range and
+  ignoreTimeRange is not true. If the pull request is not visible but the user
+  has opted to show private contributions, a RestrictedContribution will be returned.
+  """
+  firstPullRequestContribution(
+    """
+    If true, the first pull request will be returned even if it was opened outside of the collection's time range.
+
+    **Upcoming Change on 2019-07-01 UTC**
+    **Description:** `ignoreTimeRange` will be removed. Use a `ContributionsCollection` starting sufficiently far back
+    **Reason:** ignore_time_range will be removed
+
+    """
+    ignoreTimeRange: Boolean = false
+  ): CreatedPullRequestOrRestrictedContribution
+
+  """
+  The first repository the user created on GitHub. This will be null if that
+  first repository was created outside the collection's time range and
+  ignoreTimeRange is false. If the repository is not visible, then a
+  RestrictedContribution is returned.
+  """
+  firstRepositoryContribution(
+    """
+    If true, the first repository will be returned even if it was opened outside of the collection's time range.
+
+    **Upcoming Change on 2019-07-01 UTC**
+    **Description:** `ignoreTimeRange` will be removed. Use a `ContributionsCollection` starting sufficiently far back
+    **Reason:** ignore_time_range will be removed
+
+    """
+    ignoreTimeRange: Boolean = false
+  ): CreatedRepositoryOrRestrictedContribution
+
+  """
+  Does the user have any more activity in the timeline that occurred prior to the collection's time range?
+  """
+  hasActivityInThePast: Boolean!
+
+  """Determine if there are any contributions in this collection."""
+  hasAnyContributions: Boolean!
+
+  """
+  Determine if the user made any contributions in this time frame whose details
+  are not visible because they were made in a private repository. Can only be
+  true if the user enabled private contribution counts.
+  """
+  hasAnyRestrictedContributions: Boolean!
+
+  """Whether or not the collector's time span is all within the same day."""
+  isSingleDay: Boolean!
+
+  """A list of issues the user opened."""
+  issueContributions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Should the user's first issue ever be excluded from the result."""
+    excludeFirst: Boolean = false
+
+    """Should the user's most commented issue be excluded from the result."""
+    excludePopular: Boolean = false
+
+    """Ordering options for contributions returned from the connection."""
+    orderBy: ContributionOrder
+  ): CreatedIssueContributionConnection!
+
+  """Issue contributions made by the user, grouped by repository."""
+  issueContributionsByRepository(
+    """How many repositories should be included."""
+    maxRepositories: Int = 25
+
+    """Should the user's first issue ever be excluded from the result."""
+    excludeFirst: Boolean = false
+
+    """Should the user's most commented issue be excluded from the result."""
+    excludePopular: Boolean = false
+  ): [IssueContributionsByRepository!]!
+
+  """
+  When the user signed up for GitHub. This will be null if that sign up date
+  falls outside the collection's time range and ignoreTimeRange is false.
+  """
+  joinedGitHubContribution(
+    """
+    If true, the contribution will be returned even if the user signed up outside of the collection's time range.
+
+    **Upcoming Change on 2019-07-01 UTC**
+    **Description:** `ignoreTimeRange` will be removed. Use a `ContributionsCollection` starting sufficiently far back
+    **Reason:** ignore_time_range will be removed
+
+    """
+    ignoreTimeRange: Boolean = false
+  ): JoinedGitHubContribution
+
+  """
+  The date of the most recent restricted contribution the user made in this time
+  period. Can only be non-null when the user has enabled private contribution counts.
+  """
+  latestRestrictedContributionDate: Date
+
+  """
+  When this collection's time range does not include any activity from the user, use this
+  to get a different collection from an earlier time range that does have activity.
+
+  """
+  mostRecentCollectionWithActivity: ContributionsCollection
+
+  """
+  Returns a different contributions collection from an earlier time range than this one
+  that does not have any contributions.
+
+  """
+  mostRecentCollectionWithoutActivity: ContributionsCollection
+
+  """
+  The issue the user opened on GitHub that received the most comments in the specified
+  time frame.
+
+  """
+  popularIssueContribution: CreatedIssueContribution
+
+  """
+  The pull request the user opened on GitHub that received the most comments in the
+  specified time frame.
+
+  """
+  popularPullRequestContribution: CreatedPullRequestContribution
+
+  """Pull request contributions made by the user."""
+  pullRequestContributions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Should the user's first pull request ever be excluded from the result."""
+    excludeFirst: Boolean = false
+
+    """
+    Should the user's most commented pull request be excluded from the result.
+    """
+    excludePopular: Boolean = false
+
+    """Ordering options for contributions returned from the connection."""
+    orderBy: ContributionOrder
+  ): CreatedPullRequestContributionConnection!
+
+  """Pull request contributions made by the user, grouped by repository."""
+  pullRequestContributionsByRepository(
+    """How many repositories should be included."""
+    maxRepositories: Int = 25
+
+    """Should the user's first pull request ever be excluded from the result."""
+    excludeFirst: Boolean = false
+
+    """
+    Should the user's most commented pull request be excluded from the result.
+    """
+    excludePopular: Boolean = false
+  ): [PullRequestContributionsByRepository!]!
+
+  """Pull request review contributions made by the user."""
+  pullRequestReviewContributions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Ordering options for contributions returned from the connection."""
+    orderBy: ContributionOrder
+  ): CreatedPullRequestReviewContributionConnection!
+
+  """
+  Pull request review contributions made by the user, grouped by repository.
+  """
+  pullRequestReviewContributionsByRepository(
+    """How many repositories should be included."""
+    maxRepositories: Int = 25
+  ): [PullRequestReviewContributionsByRepository!]!
+
+  """
+  A list of repositories owned by the user that the user created in this time range.
+  """
+  repositoryContributions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Should the user's first repository ever be excluded from the result."""
+    excludeFirst: Boolean = false
+
+    """Ordering options for contributions returned from the connection."""
+    orderBy: ContributionOrder
+  ): CreatedRepositoryContributionConnection!
+
+  """
+  A count of contributions made by the user that the viewer cannot access. Only
+  non-zero when the user has chosen to share their private contribution counts.
+  """
+  restrictedContributionsCount: Int!
+
+  """The beginning date and time of this collection."""
+  startedAt: DateTime!
+
+  """How many commits were made by the user in this time span."""
+  totalCommitContributions: Int!
+
+  """How many issues the user opened."""
+  totalIssueContributions(
+    """Should the user's first issue ever be excluded from this count."""
+    excludeFirst: Boolean = false
+
+    """Should the user's most commented issue be excluded from this count."""
+    excludePopular: Boolean = false
+  ): Int!
+
+  """How many pull requests the user opened."""
+  totalPullRequestContributions(
+    """Should the user's first pull request ever be excluded from this count."""
+    excludeFirst: Boolean = false
+
+    """
+    Should the user's most commented pull request be excluded from this count.
+    """
+    excludePopular: Boolean = false
+  ): Int!
+
+  """How many pull request reviews the user left."""
+  totalPullRequestReviewContributions: Int!
+
+  """How many different repositories the user committed to."""
+  totalRepositoriesWithContributedCommits: Int!
+
+  """How many different repositories the user opened issues in."""
+  totalRepositoriesWithContributedIssues(
+    """Should the user's first issue ever be excluded from this count."""
+    excludeFirst: Boolean = false
+
+    """Should the user's most commented issue be excluded from this count."""
+    excludePopular: Boolean = false
+  ): Int!
+
+  """How many different repositories the user left pull request reviews in."""
+  totalRepositoriesWithContributedPullRequestReviews: Int!
+
+  """How many different repositories the user opened pull requests in."""
+  totalRepositoriesWithContributedPullRequests(
+    """Should the user's first pull request ever be excluded from this count."""
+    excludeFirst: Boolean = false
+
+    """
+    Should the user's most commented pull request be excluded from this count.
+    """
+    excludePopular: Boolean = false
+  ): Int!
+
+  """How many repositories the user created."""
+  totalRepositoryContributions(
+    """Should the user's first repository ever be excluded from this count."""
+    excludeFirst: Boolean = false
+  ): Int!
+
+  """The user who made the contributions in this collection."""
+  user: User!
+}
+
+"""
+Represents a 'converted_note_to_issue' event on a given issue or pull request.
+"""
+type ConvertedNoteToIssueEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+}
+
+"""Autogenerated input type of ConvertProjectCardNoteToIssue"""
+input ConvertProjectCardNoteToIssueInput {
+  """The ProjectCard ID to convert."""
+  projectCardId: ID!
+
+  """The ID of the repository to create the issue in."""
+  repositoryId: ID!
+
+  """
+  The title of the newly created issue. Defaults to the card's note text.
+  """
+  title: String
+
+  """The body of the newly created issue."""
+  body: String
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of ConvertProjectCardNoteToIssue"""
+type ConvertProjectCardNoteToIssuePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The updated ProjectCard."""
+  projectCard: ProjectCard
+}
+
+"""Autogenerated input type of CreateBranchProtectionRule"""
+input CreateBranchProtectionRuleInput {
+  """
+  The global relay id of the repository in which a new branch protection rule should be created in.
+  """
+  repositoryId: ID!
+
+  """The glob-like pattern used to determine matching branches."""
+  pattern: String!
+
+  """Are approving reviews required to update matching branches."""
+  requiresApprovingReviews: Boolean
+
+  """Number of approving reviews required to update matching branches."""
+  requiredApprovingReviewCount: Int
+
+  """Are commits required to be signed."""
+  requiresCommitSignatures: Boolean
+
+  """Can admins overwrite branch protection."""
+  isAdminEnforced: Boolean
+
+  """Are status checks required to update matching branches."""
+  requiresStatusChecks: Boolean
+
+  """Are branches required to be up to date before merging."""
+  requiresStrictStatusChecks: Boolean
+
+  """Are reviews from code owners required to update matching branches."""
+  requiresCodeOwnerReviews: Boolean
+
+  """
+  Will new commits pushed to matching branches dismiss pull request review approvals.
+  """
+  dismissesStaleReviews: Boolean
+
+  """Is dismissal of pull request reviews restricted."""
+  restrictsReviewDismissals: Boolean
+
+  """
+  A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.
+  """
+  reviewDismissalActorIds: [ID!]
+
+  """Is pushing to matching branches restricted."""
+  restrictsPushes: Boolean
+
+  """A list of User or Team IDs allowed to push to matching branches."""
+  pushActorIds: [ID!]
+
+  """
+  List of required status check contexts that must pass for commits to be accepted to matching branches.
+  """
+  requiredStatusCheckContexts: [String!]
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of CreateBranchProtectionRule"""
+type CreateBranchProtectionRulePayload {
+  """The newly created BranchProtectionRule."""
+  branchProtectionRule: BranchProtectionRule
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated input type of CreateContentAttachment"""
+input CreateContentAttachmentInput {
+  """The node ID of the content_reference."""
+  contentReferenceId: ID!
+
+  """The title of the content attachment."""
+  title: String!
+
+  """The body of the content attachment, which may contain markdown."""
+  body: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Represents the contribution a user made by committing to a repository."""
+type CreatedCommitContribution implements Contribution {
+  """How many commits were made on this day to this repository by the user."""
+  commitCount: Int!
+
+  """
+  Whether this contribution is associated with a record you do not have access to. For
+  example, your own 'first issue' contribution may have been made on a repository you can no
+  longer access.
+
+  """
+  isRestricted: Boolean!
+
+  """When this contribution was made."""
+  occurredAt: DateTime!
+
+  """The repository the user made a commit in."""
+  repository: Repository!
+
+  """The HTTP path for this contribution."""
+  resourcePath: URI!
+
+  """The HTTP URL for this contribution."""
+  url: URI!
+
+  """
+  The user who made this contribution.
+
+  """
+  user: User!
+}
+
+"""The connection type for CreatedCommitContribution."""
+type CreatedCommitContributionConnection {
+  """A list of edges."""
+  edges: [CreatedCommitContributionEdge]
+
+  """A list of nodes."""
+  nodes: [CreatedCommitContribution]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """
+  Identifies the total count of commits across days and repositories in the connection.
+
+  """
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type CreatedCommitContributionEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: CreatedCommitContribution
+}
+
+"""Represents the contribution a user made on GitHub by opening an issue."""
+type CreatedIssueContribution implements Contribution {
+  """
+  Whether this contribution is associated with a record you do not have access to. For
+  example, your own 'first issue' contribution may have been made on a repository you can no
+  longer access.
+
+  """
+  isRestricted: Boolean!
+
+  """The issue that was opened."""
+  issue: Issue!
+
+  """When this contribution was made."""
+  occurredAt: DateTime!
+
+  """The HTTP path for this contribution."""
+  resourcePath: URI!
+
+  """The HTTP URL for this contribution."""
+  url: URI!
+
+  """
+  The user who made this contribution.
+
+  """
+  user: User!
+}
+
+"""The connection type for CreatedIssueContribution."""
+type CreatedIssueContributionConnection {
+  """A list of edges."""
+  edges: [CreatedIssueContributionEdge]
+
+  """A list of nodes."""
+  nodes: [CreatedIssueContribution]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type CreatedIssueContributionEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: CreatedIssueContribution
+}
+
+"""
+Represents either a issue the viewer can access or a restricted contribution.
+"""
+union CreatedIssueOrRestrictedContribution = CreatedIssueContribution | RestrictedContribution
+
+"""
+Represents the contribution a user made on GitHub by opening a pull request.
+"""
+type CreatedPullRequestContribution implements Contribution {
+  """
+  Whether this contribution is associated with a record you do not have access to. For
+  example, your own 'first issue' contribution may have been made on a repository you can no
+  longer access.
+
+  """
+  isRestricted: Boolean!
+
+  """When this contribution was made."""
+  occurredAt: DateTime!
+
+  """The pull request that was opened."""
+  pullRequest: PullRequest!
+
+  """The HTTP path for this contribution."""
+  resourcePath: URI!
+
+  """The HTTP URL for this contribution."""
+  url: URI!
+
+  """
+  The user who made this contribution.
+
+  """
+  user: User!
+}
+
+"""The connection type for CreatedPullRequestContribution."""
+type CreatedPullRequestContributionConnection {
+  """A list of edges."""
+  edges: [CreatedPullRequestContributionEdge]
+
+  """A list of nodes."""
+  nodes: [CreatedPullRequestContribution]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type CreatedPullRequestContributionEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: CreatedPullRequestContribution
+}
+
+"""
+Represents either a pull request the viewer can access or a restricted contribution.
+"""
+union CreatedPullRequestOrRestrictedContribution = CreatedPullRequestContribution | RestrictedContribution
+
+"""
+Represents the contribution a user made by leaving a review on a pull request.
+"""
+type CreatedPullRequestReviewContribution implements Contribution {
+  """
+  Whether this contribution is associated with a record you do not have access to. For
+  example, your own 'first issue' contribution may have been made on a repository you can no
+  longer access.
+
+  """
+  isRestricted: Boolean!
+
+  """When this contribution was made."""
+  occurredAt: DateTime!
+
+  """The pull request the user reviewed."""
+  pullRequest: PullRequest!
+
+  """The review the user left on the pull request."""
+  pullRequestReview: PullRequestReview!
+
+  """The repository containing the pull request that the user reviewed."""
+  repository: Repository!
+
+  """The HTTP path for this contribution."""
+  resourcePath: URI!
+
+  """The HTTP URL for this contribution."""
+  url: URI!
+
+  """
+  The user who made this contribution.
+
+  """
+  user: User!
+}
+
+"""The connection type for CreatedPullRequestReviewContribution."""
+type CreatedPullRequestReviewContributionConnection {
+  """A list of edges."""
+  edges: [CreatedPullRequestReviewContributionEdge]
+
+  """A list of nodes."""
+  nodes: [CreatedPullRequestReviewContribution]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type CreatedPullRequestReviewContributionEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: CreatedPullRequestReviewContribution
+}
+
+"""
+Represents the contribution a user made on GitHub by creating a repository.
+"""
+type CreatedRepositoryContribution implements Contribution {
+  """
+  Whether this contribution is associated with a record you do not have access to. For
+  example, your own 'first issue' contribution may have been made on a repository you can no
+  longer access.
+
+  """
+  isRestricted: Boolean!
+
+  """When this contribution was made."""
+  occurredAt: DateTime!
+
+  """The repository that was created."""
+  repository: Repository!
+
+  """The HTTP path for this contribution."""
+  resourcePath: URI!
+
+  """The HTTP URL for this contribution."""
+  url: URI!
+
+  """
+  The user who made this contribution.
+
+  """
+  user: User!
+}
+
+"""The connection type for CreatedRepositoryContribution."""
+type CreatedRepositoryContributionConnection {
+  """A list of edges."""
+  edges: [CreatedRepositoryContributionEdge]
+
+  """A list of nodes."""
+  nodes: [CreatedRepositoryContribution]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type CreatedRepositoryContributionEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: CreatedRepositoryContribution
+}
+
+"""
+Represents either a repository the viewer can access or a restricted contribution.
+"""
+union CreatedRepositoryOrRestrictedContribution = CreatedRepositoryContribution | RestrictedContribution
+
+"""Autogenerated input type of CreateIssue"""
+input CreateIssueInput {
+  """The Node ID of the repository."""
+  repositoryId: ID!
+
+  """The title for the issue."""
+  title: String!
+
+  """The body for the issue description."""
+  body: String
+
+  """The Node ID for the user assignee for this issue."""
+  assigneeIds: [ID!]
+
+  """The Node ID of the milestone for this issue."""
+  milestoneId: ID
+
+  """An array of Node IDs of labels for this issue."""
+  labelIds: [ID!]
+
+  """An array of Node IDs for projects associated with this issue."""
+  projectIds: [ID!]
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of CreateIssue"""
+type CreateIssuePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The new issue."""
+  issue: Issue
+}
+
+"""Autogenerated input type of CreateProject"""
+input CreateProjectInput {
+  """The owner ID to create the project under."""
+  ownerId: ID!
+
+  """The name of project."""
+  name: String!
+
+  """The description of project."""
+  body: String
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of CreateProject"""
+type CreateProjectPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The new project."""
+  project: Project
+}
+
+"""Autogenerated input type of CreatePullRequest"""
+input CreatePullRequestInput {
+  """The Node ID of the repository."""
+  repositoryId: ID!
+
+  """
+  The name of the branch you want your changes pulled into. This should be an existing branch
+  on the current repository. You cannot update the base branch on a pull request to point
+  to another repository.
+
+  """
+  baseRefName: String!
+
+  """
+  The name of the branch where your changes are implemented. For cross-repository pull requests
+  in the same network, namespace `head_ref_name` with a user like this: `username:branch`.
+
+  """
+  headRefName: String!
+
+  """The title of the pull request."""
+  title: String!
+
+  """The contents of the pull request."""
+  body: String
+
+  """Indicates whether maintainers can modify the pull request."""
+  maintainerCanModify: Boolean = true
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of CreatePullRequest"""
+type CreatePullRequestPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The new pull request."""
+  pullRequest: PullRequest
+}
+
+"""Represents a mention made by one issue or pull request to another."""
+type CrossReferencedEvent implements Node & UniformResourceLocatable {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Reference originated in a different repository."""
+  isCrossRepository: Boolean!
+
+  """Identifies when the reference was made."""
+  referencedAt: DateTime!
+
+  """The HTTP path for this pull request."""
+  resourcePath: URI!
+
+  """Issue or pull request that made the reference."""
+  source: ReferencedSubject!
+
+  """Issue or pull request to which the reference was made."""
+  target: ReferencedSubject!
+
+  """The HTTP URL for this pull request."""
+  url: URI!
+
+  """Checks if the target will be closed when the source is merged."""
+  willCloseTarget: Boolean!
+}
+
+"""An ISO-8601 encoded date string."""
+scalar Date
+
+"""An ISO-8601 encoded UTC date string."""
+scalar DateTime
+
+"""Autogenerated input type of DeclineTopicSuggestion"""
+input DeclineTopicSuggestionInput {
+  """The Node ID of the repository."""
+  repositoryId: ID!
+
+  """The name of the suggested topic."""
+  name: String!
+
+  """The reason why the suggested topic is declined."""
+  reason: TopicSuggestionDeclineReason!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeclineTopicSuggestion"""
+type DeclineTopicSuggestionPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The declined topic."""
+  topic: Topic
+}
+
+"""The possible default permissions for repositories."""
+enum DefaultRepositoryPermissionField {
+  """No access"""
+  NONE
+
+  """Can read repos by default"""
+  READ
+
+  """Can read and write repos by default"""
+  WRITE
+
+  """Can read, write, and administrate repos by default"""
+  ADMIN
+}
+
+"""Entities that can be deleted."""
+interface Deletable {
+  """Check if the current viewer can delete this object."""
+  viewerCanDelete: Boolean!
+}
+
+"""Autogenerated input type of DeleteBranchProtectionRule"""
+input DeleteBranchProtectionRuleInput {
+  """The global relay id of the branch protection rule to be deleted."""
+  branchProtectionRuleId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeleteBranchProtectionRule"""
+type DeleteBranchProtectionRulePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated input type of DeleteIssueComment"""
+input DeleteIssueCommentInput {
+  """The ID of the comment to delete."""
+  id: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeleteIssueComment"""
+type DeleteIssueCommentPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated input type of DeleteIssue"""
+input DeleteIssueInput {
+  """The ID of the issue to delete."""
+  issueId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeleteIssue"""
+type DeleteIssuePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The repository the issue belonged to"""
+  repository: Repository
+}
+
+"""Autogenerated input type of DeleteProjectCard"""
+input DeleteProjectCardInput {
+  """The id of the card to delete."""
+  cardId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeleteProjectCard"""
+type DeleteProjectCardPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The column the deleted card was in."""
+  column: ProjectColumn
+
+  """The deleted card ID."""
+  deletedCardId: ID
+}
+
+"""Autogenerated input type of DeleteProjectColumn"""
+input DeleteProjectColumnInput {
+  """The id of the column to delete."""
+  columnId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeleteProjectColumn"""
+type DeleteProjectColumnPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The deleted column ID."""
+  deletedColumnId: ID
+
+  """The project the deleted column was in."""
+  project: Project
+}
+
+"""Autogenerated input type of DeleteProject"""
+input DeleteProjectInput {
+  """The Project ID to update."""
+  projectId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeleteProject"""
+type DeleteProjectPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The repository or organization the project was removed from."""
+  owner: ProjectOwner
+}
+
+"""Autogenerated input type of DeletePullRequestReviewComment"""
+input DeletePullRequestReviewCommentInput {
+  """The ID of the comment to delete."""
+  id: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeletePullRequestReviewComment"""
+type DeletePullRequestReviewCommentPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The pull request review the deleted comment belonged to."""
+  pullRequestReview: PullRequestReview
+}
+
+"""Autogenerated input type of DeletePullRequestReview"""
+input DeletePullRequestReviewInput {
+  """The Node ID of the pull request review to delete."""
+  pullRequestReviewId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DeletePullRequestReview"""
+type DeletePullRequestReviewPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The deleted pull request review."""
+  pullRequestReview: PullRequestReview
+}
+
+"""Represents a 'demilestoned' event on a given issue or pull request."""
+type DemilestonedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """
+  Identifies the milestone title associated with the 'demilestoned' event.
+  """
+  milestoneTitle: String!
+
+  """Object referenced by event."""
+  subject: MilestoneItem!
+}
+
+"""Represents a 'deployed' event on a given pull request."""
+type DeployedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The deployment associated with the 'deployed' event."""
+  deployment: Deployment!
+  id: ID!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+
+  """The ref associated with the 'deployed' event."""
+  ref: Ref
+}
+
+"""A repository deploy key."""
+type DeployKey implements Node {
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """The deploy key."""
+  key: String!
+
+  """Whether or not the deploy key is read only."""
+  readOnly: Boolean!
+
+  """The deploy key title."""
+  title: String!
+
+  """Whether or not the deploy key has been verified."""
+  verified: Boolean!
+}
+
+"""The connection type for DeployKey."""
+type DeployKeyConnection {
+  """A list of edges."""
+  edges: [DeployKeyEdge]
+
+  """A list of nodes."""
+  nodes: [DeployKey]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type DeployKeyEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: DeployKey
+}
+
+"""Represents triggered deployment instance."""
+type Deployment implements Node {
+  """Identifies the commit sha of the deployment."""
+  commit: Commit
+
+  """
+  Identifies the oid of the deployment commit, even if the commit has been deleted.
+  """
+  commitOid: String!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the actor who triggered the deployment."""
+  creator: Actor
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The deployment description."""
+  description: String
+
+  """The environment to which this deployment was made."""
+  environment: String
+  id: ID!
+
+  """The latest status of this deployment."""
+  latestStatus: DeploymentStatus
+
+  """Extra information that a deployment system might need."""
+  payload: String
+
+  """
+  Identifies the Ref of the deployment, if the deployment was created by ref.
+  """
+  ref: Ref
+
+  """Identifies the repository associated with the deployment."""
+  repository: Repository!
+
+  """The current state of the deployment."""
+  state: DeploymentState
+
+  """A list of statuses associated with the deployment."""
+  statuses(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): DeploymentStatusConnection
+
+  """The deployment task."""
+  task: String
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+}
+
+"""The connection type for Deployment."""
+type DeploymentConnection {
+  """A list of edges."""
+  edges: [DeploymentEdge]
+
+  """A list of nodes."""
+  nodes: [Deployment]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type DeploymentEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Deployment
+}
+
+"""
+Represents a 'deployment_environment_changed' event on a given pull request.
+"""
+type DeploymentEnvironmentChangedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The deployment status that updated the deployment environment."""
+  deploymentStatus: DeploymentStatus!
+  id: ID!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+}
+
+"""Ordering options for deployment connections"""
+input DeploymentOrder {
+  """The field to order deployments by."""
+  field: DeploymentOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which deployment connections can be ordered."""
+enum DeploymentOrderField {
+  """Order collection by creation time"""
+  CREATED_AT
+}
+
+"""The possible states in which a deployment can be."""
+enum DeploymentState {
+  """The pending deployment was not updated after 30 minutes."""
+  ABANDONED
+
+  """The deployment is currently active."""
+  ACTIVE
+
+  """An inactive transient deployment."""
+  DESTROYED
+
+  """The deployment experienced an error."""
+  ERROR
+
+  """The deployment has failed."""
+  FAILURE
+
+  """The deployment is inactive."""
+  INACTIVE
+
+  """The deployment is pending."""
+  PENDING
+
+  """The deployment has queued"""
+  QUEUED
+
+  """The deployment is in progress."""
+  IN_PROGRESS
+}
+
+"""Describes the status of a given deployment attempt."""
+type DeploymentStatus implements Node {
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the actor who triggered the deployment."""
+  creator: Actor
+
+  """Identifies the deployment associated with status."""
+  deployment: Deployment!
+
+  """Identifies the description of the deployment."""
+  description: String
+
+  """Identifies the environment URL of the deployment."""
+  environmentUrl: URI
+  id: ID!
+
+  """Identifies the log URL of the deployment."""
+  logUrl: URI
+
+  """Identifies the current state of the deployment."""
+  state: DeploymentStatusState!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+}
+
+"""The connection type for DeploymentStatus."""
+type DeploymentStatusConnection {
+  """A list of edges."""
+  edges: [DeploymentStatusEdge]
+
+  """A list of nodes."""
+  nodes: [DeploymentStatus]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type DeploymentStatusEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: DeploymentStatus
+}
+
+"""The possible states for a deployment status."""
+enum DeploymentStatusState {
+  """The deployment is pending."""
+  PENDING
+
+  """The deployment was successful."""
+  SUCCESS
+
+  """The deployment has failed."""
+  FAILURE
+
+  """The deployment is inactive."""
+  INACTIVE
+
+  """The deployment experienced an error."""
+  ERROR
+
+  """The deployment is queued"""
+  QUEUED
+
+  """The deployment is in progress."""
+  IN_PROGRESS
+}
+
+"""Autogenerated input type of DismissPullRequestReview"""
+input DismissPullRequestReviewInput {
+  """The Node ID of the pull request review to modify."""
+  pullRequestReviewId: ID!
+
+  """The contents of the pull request review dismissal message."""
+  message: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of DismissPullRequestReview"""
+type DismissPullRequestReviewPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The dismissed pull request review."""
+  pullRequestReview: PullRequestReview
+}
+
+"""Specifies a review comment to be left with a Pull Request Review."""
+input DraftPullRequestReviewComment {
+  """Path to the file being commented on."""
+  path: String!
+
+  """Position in the file to leave a comment on."""
+  position: Int!
+
+  """Body of the comment to leave."""
+  body: String!
+}
+
+"""An external identity provisioned by SAML SSO or SCIM."""
+type ExternalIdentity implements Node {
+  """The GUID for this identity"""
+  guid: String!
+  id: ID!
+
+  """Organization invitation for this SCIM-provisioned external identity"""
+  organizationInvitation: OrganizationInvitation
+
+  """SAML Identity attributes"""
+  samlIdentity: ExternalIdentitySamlAttributes
+
+  """SCIM Identity attributes"""
+  scimIdentity: ExternalIdentityScimAttributes
+
+  """
+  User linked to this external identity. Will be NULL if this identity has not been claimed by an organization member.
+  """
+  user: User
+}
+
+"""The connection type for ExternalIdentity."""
+type ExternalIdentityConnection {
+  """A list of edges."""
+  edges: [ExternalIdentityEdge]
+
+  """A list of nodes."""
+  nodes: [ExternalIdentity]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type ExternalIdentityEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: ExternalIdentity
+}
+
+"""SAML attributes for the External Identity"""
+type ExternalIdentitySamlAttributes {
+  """The NameID of the SAML identity"""
+  nameId: String
+}
+
+"""SCIM attributes for the External Identity"""
+type ExternalIdentityScimAttributes {
+  """The userName of the SCIM identity"""
+  username: String
+}
+
+"""The connection type for User."""
+type FollowerConnection {
+  """A list of edges."""
+  edges: [UserEdge]
+
+  """A list of nodes."""
+  nodes: [User]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""The connection type for User."""
+type FollowingConnection {
+  """A list of edges."""
+  edges: [UserEdge]
+
+  """A list of nodes."""
+  nodes: [User]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""A Gist."""
+type Gist implements Node & Starrable {
+  """A list of comments associated with the gist"""
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): GistCommentConnection!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The gist description."""
+  description: String
+
+  """The files in this gist."""
+  files(
+    """The maximum number of files to return."""
+    limit: Int = 10
+  ): [GistFile]
+  id: ID!
+
+  """Identifies if the gist is a fork."""
+  isFork: Boolean!
+
+  """Whether the gist is public or not."""
+  isPublic: Boolean!
+
+  """The gist name."""
+  name: String!
+
+  """The gist owner."""
+  owner: RepositoryOwner
+
+  """Identifies when the gist was last pushed to."""
+  pushedAt: DateTime
+
+  """A list of users who have starred this starrable."""
+  stargazers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Order for connection"""
+    orderBy: StarOrder
+  ): StargazerConnection!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """
+  Returns a boolean indicating whether the viewing user has starred this starrable.
+  """
+  viewerHasStarred: Boolean!
+}
+
+"""Represents a comment on an Gist."""
+type GistComment implements Node & Comment & Deletable & Updatable & UpdatableComment {
+  """The actor who authored the comment."""
+  author: Actor
+
+  """Author's association with the gist."""
+  authorAssociation: CommentAuthorAssociation!
+
+  """Identifies the comment body."""
+  body: String!
+
+  """The comment body rendered to HTML."""
+  bodyHTML: HTML!
+
+  """The body rendered to text."""
+  bodyText: String!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Check if this comment was created via an email reply."""
+  createdViaEmail: Boolean!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The actor who edited the comment."""
+  editor: Actor
+
+  """The associated gist."""
+  gist: Gist!
+  id: ID!
+
+  """
+  Check if this comment was edited and includes an edit with the creation data
+  """
+  includesCreatedEdit: Boolean!
+
+  """Returns whether or not a comment has been minimized."""
+  isMinimized: Boolean!
+
+  """The moment the editor made the last edit"""
+  lastEditedAt: DateTime
+
+  """Returns why the comment was minimized."""
+  minimizedReason: String
+
+  """Identifies when the comment was published at."""
+  publishedAt: DateTime
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """A list of edits to this content."""
+  userContentEdits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserContentEditConnection
+
+  """Check if the current viewer can delete this object."""
+  viewerCanDelete: Boolean!
+
+  """Check if the current viewer can minimize this object."""
+  viewerCanMinimize: Boolean!
+
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+
+  """Reasons why the current viewer can not update this comment."""
+  viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+  """Did the viewer author this comment."""
+  viewerDidAuthor: Boolean!
+}
+
+"""The connection type for GistComment."""
+type GistCommentConnection {
+  """A list of edges."""
+  edges: [GistCommentEdge]
+
+  """A list of nodes."""
+  nodes: [GistComment]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type GistCommentEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: GistComment
+}
+
+"""The connection type for Gist."""
+type GistConnection {
+  """A list of edges."""
+  edges: [GistEdge]
+
+  """A list of nodes."""
+  nodes: [Gist]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type GistEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Gist
+}
+
+"""A file in a gist."""
+type GistFile {
+  """
+  The file name encoded to remove characters that are invalid in URL paths.
+  """
+  encodedName: String
+
+  """The gist file encoding."""
+  encoding: String
+
+  """The file extension from the file name."""
+  extension: String
+
+  """Indicates if this file is an image."""
+  isImage: Boolean!
+
+  """Whether the file's contents were truncated."""
+  isTruncated: Boolean!
+
+  """The programming language this file is written in."""
+  language: Language
+
+  """The gist file name."""
+  name: String
+
+  """The gist file size in bytes."""
+  size: Int
+
+  """UTF8 text data or null if the file is binary"""
+  text(
+    """Optionally truncate the returned file to this length."""
+    truncate: Int
+  ): String
+}
+
+"""Ordering options for gist connections"""
+input GistOrder {
+  """The field to order repositories by."""
+  field: GistOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which gist connections can be ordered."""
+enum GistOrderField {
+  """Order gists by creation time"""
+  CREATED_AT
+
+  """Order gists by update time"""
+  UPDATED_AT
+
+  """Order gists by push time"""
+  PUSHED_AT
+}
+
+"""The privacy of a Gist"""
+enum GistPrivacy {
+  """Public"""
+  PUBLIC
+
+  """Secret"""
+  SECRET
+
+  """Gists that are public and secret"""
+  ALL
+}
+
+"""Represents an actor in a Git commit (ie. an author or committer)."""
+type GitActor {
+  """A URL pointing to the author's public avatar."""
+  avatarUrl(
+    """The size of the resulting square image."""
+    size: Int
+  ): URI!
+
+  """The timestamp of the Git action (authoring or committing)."""
+  date: GitTimestamp
+
+  """The email in the Git commit."""
+  email: String
+
+  """The name in the Git commit."""
+  name: String
+
+  """
+  The GitHub user corresponding to the email field. Null if no such user exists.
+  """
+  user: User
+}
+
+"""Represents information about the GitHub instance."""
+type GitHubMetadata {
+  """Returns a String that's a SHA of `github-services`"""
+  gitHubServicesSha: GitObjectID!
+
+  """IP addresses that users connect to for git operations"""
+  gitIpAddresses: [String!]
+
+  """IP addresses that service hooks are sent from"""
+  hookIpAddresses: [String!]
+
+  """IP addresses that the importer connects from"""
+  importerIpAddresses: [String!]
+
+  """Whether or not users are verified"""
+  isPasswordAuthenticationVerifiable: Boolean!
+
+  """IP addresses for GitHub Pages' A records"""
+  pagesIpAddresses: [String!]
+}
+
+"""Represents a Git object."""
+interface GitObject {
+  """An abbreviated version of the Git object ID"""
+  abbreviatedOid: String!
+
+  """The HTTP path for this Git object"""
+  commitResourcePath: URI!
+
+  """The HTTP URL for this Git object"""
+  commitUrl: URI!
+  id: ID!
+
+  """The Git object ID"""
+  oid: GitObjectID!
+
+  """The Repository the Git object belongs to"""
+  repository: Repository!
+}
+
+"""A Git object ID."""
+scalar GitObjectID
+
+"""Information about a signature (GPG or S/MIME) on a Commit or Tag."""
+interface GitSignature {
+  """Email used to sign this object."""
+  email: String!
+
+  """True if the signature is valid and verified by GitHub."""
+  isValid: Boolean!
+
+  """
+  Payload for GPG signing object. Raw ODB object without the signature header.
+  """
+  payload: String!
+
+  """ASCII-armored signature header from object."""
+  signature: String!
+
+  """GitHub user corresponding to the email signing this commit."""
+  signer: User
+
+  """
+  The state of this signature. `VALID` if signature is valid and verified by
+  GitHub, otherwise represents reason why signature is considered invalid.
+  """
+  state: GitSignatureState!
+
+  """True if the signature was made with GitHub's signing key."""
+  wasSignedByGitHub: Boolean!
+}
+
+"""The state of a Git signature."""
+enum GitSignatureState {
+  """Valid signature and verified by GitHub"""
+  VALID
+
+  """Invalid signature"""
+  INVALID
+
+  """Malformed signature"""
+  MALFORMED_SIG
+
+  """Key used for signing not known to GitHub"""
+  UNKNOWN_KEY
+
+  """Invalid email used for signing"""
+  BAD_EMAIL
+
+  """Email used for signing unverified on GitHub"""
+  UNVERIFIED_EMAIL
+
+  """Email used for signing not known to GitHub"""
+  NO_USER
+
+  """Unknown signature type"""
+  UNKNOWN_SIG_TYPE
+
+  """Unsigned"""
+  UNSIGNED
+
+  """
+  Internal error - the GPG verification service is unavailable at the moment
+  """
+  GPGVERIFY_UNAVAILABLE
+
+  """Internal error - the GPG verification service misbehaved"""
+  GPGVERIFY_ERROR
+
+  """The usage flags for the key that signed this don't allow signing"""
+  NOT_SIGNING_KEY
+
+  """Signing key expired"""
+  EXPIRED_KEY
+
+  """Valid signature, pending certificate revocation checking"""
+  OCSP_PENDING
+
+  """Valid siganture, though certificate revocation check failed"""
+  OCSP_ERROR
+
+  """The signing certificate or its chain could not be verified"""
+  BAD_CERT
+
+  """One or more certificates in chain has been revoked"""
+  OCSP_REVOKED
+}
+
+"""Git SSH string"""
+scalar GitSSHRemote
+
+"""
+An ISO-8601 encoded date string. Unlike the DateTime type, GitTimestamp is not converted in UTC.
+"""
+scalar GitTimestamp
+
+"""Represents a GPG signature on a Commit or Tag."""
+type GpgSignature implements GitSignature {
+  """Email used to sign this object."""
+  email: String!
+
+  """True if the signature is valid and verified by GitHub."""
+  isValid: Boolean!
+
+  """Hex-encoded ID of the key that signed this object."""
+  keyId: String
+
+  """
+  Payload for GPG signing object. Raw ODB object without the signature header.
+  """
+  payload: String!
+
+  """ASCII-armored signature header from object."""
+  signature: String!
+
+  """GitHub user corresponding to the email signing this commit."""
+  signer: User
+
+  """
+  The state of this signature. `VALID` if signature is valid and verified by
+  GitHub, otherwise represents reason why signature is considered invalid.
+  """
+  state: GitSignatureState!
+
+  """True if the signature was made with GitHub's signing key."""
+  wasSignedByGitHub: Boolean!
+}
+
+"""Represents a 'head_ref_deleted' event on a given pull request."""
+type HeadRefDeletedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the Ref associated with the `head_ref_deleted` event."""
+  headRef: Ref
+
+  """
+  Identifies the name of the Ref associated with the `head_ref_deleted` event.
+  """
+  headRefName: String!
+  id: ID!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+}
+
+"""Represents a 'head_ref_force_pushed' event on a given pull request."""
+type HeadRefForcePushedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the after commit SHA for the 'head_ref_force_pushed' event."""
+  afterCommit: Commit
+
+  """
+  Identifies the before commit SHA for the 'head_ref_force_pushed' event.
+  """
+  beforeCommit: Commit
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+
+  """
+  Identifies the fully qualified ref name for the 'head_ref_force_pushed' event.
+  """
+  ref: Ref
+}
+
+"""Represents a 'head_ref_restored' event on a given pull request."""
+type HeadRefRestoredEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+}
+
+"""A string containing HTML code."""
+scalar HTML
+
+"""
+The possible states in which authentication can be configured with an identity provider.
+"""
+enum IdentityProviderConfigurationState {
+  """Authentication with an identity provider is configured and enforced."""
+  ENFORCED
+
+  """
+  Authentication with an identity provider is configured but not enforced.
+  """
+  CONFIGURED
+
+  """Authentication with an identity provider is not configured."""
+  UNCONFIGURED
+}
+
+"""Autogenerated input type of ImportProject"""
+input ImportProjectInput {
+  """The name of the Organization or User to create the Project under."""
+  ownerName: String!
+
+  """The name of Project."""
+  name: String!
+
+  """The description of Project."""
+  body: String
+
+  """Whether the Project is public or not."""
+  public: Boolean = false
+
+  """A list of columns containing issues and pull requests."""
+  columnImports: [ProjectColumnImport!]!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""
+An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project.
+"""
+type Issue implements Node & Assignable & Closable & Comment & Updatable & UpdatableComment & Labelable & Lockable & Reactable & RepositoryNode & Subscribable & UniformResourceLocatable {
+  """Reason that the conversation was locked."""
+  activeLockReason: LockReason
+
+  """A list of Users assigned to this object."""
+  assignees(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+
+  """The actor who authored the comment."""
+  author: Actor
+
+  """Author's association with the subject of the comment."""
+  authorAssociation: CommentAuthorAssociation!
+
+  """Identifies the body of the issue."""
+  body: String!
+
+  """Identifies the body of the issue rendered to HTML."""
+  bodyHTML: HTML!
+
+  """Identifies the body of the issue rendered to text."""
+  bodyText: String!
+
+  """
+  `true` if the object is closed (definition of closed may depend on type)
+  """
+  closed: Boolean!
+
+  """Identifies the date and time when the object was closed."""
+  closedAt: DateTime
+
+  """A list of comments associated with the Issue."""
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueCommentConnection!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Check if this comment was created via an email reply."""
+  createdViaEmail: Boolean!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The actor who edited the comment."""
+  editor: Actor
+  id: ID!
+
+  """
+  Check if this comment was edited and includes an edit with the creation data
+  """
+  includesCreatedEdit: Boolean!
+
+  """A list of labels associated with the object."""
+  labels(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): LabelConnection
+
+  """The moment the editor made the last edit"""
+  lastEditedAt: DateTime
+
+  """`true` if the object is locked"""
+  locked: Boolean!
+
+  """Identifies the milestone associated with the issue."""
+  milestone: Milestone
+
+  """Identifies the issue number."""
+  number: Int!
+
+  """A list of Users that are participating in the Issue conversation."""
+  participants(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+
+  """List of project cards associated with this issue."""
+  projectCards(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """A list of archived states to filter the cards by"""
+    archivedStates: [ProjectCardArchivedState]
+  ): ProjectCardConnection!
+
+  """Identifies when the comment was published at."""
+  publishedAt: DateTime
+
+  """A list of reactions grouped by content left on the subject."""
+  reactionGroups: [ReactionGroup!]
+
+  """A list of Reactions left on the Issue."""
+  reactions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Allows filtering Reactions by emoji."""
+    content: ReactionContent
+
+    """Allows specifying the order in which reactions are returned."""
+    orderBy: ReactionOrder
+  ): ReactionConnection!
+
+  """The repository associated with this node."""
+  repository: Repository!
+
+  """The HTTP path for this issue"""
+  resourcePath: URI!
+
+  """Identifies the state of the issue."""
+  state: IssueState!
+
+  """A list of events, comments, commits, etc. associated with the issue."""
+  timeline(
+    """Allows filtering timeline events by a `since` timestamp."""
+    since: DateTime
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueTimelineConnection!
+
+  """A list of events, comments, commits, etc. associated with the issue."""
+  timelineItems(
+    """Filter timeline items by a `since` timestamp."""
+    since: DateTime
+
+    """Skips the first _n_ elements in the list."""
+    skip: Int
+
+    """Filter timeline items by type."""
+    itemTypes: [IssueTimelineItemsItemType!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueTimelineItemsConnection!
+
+  """Identifies the issue title."""
+  title: String!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this issue"""
+  url: URI!
+
+  """A list of edits to this content."""
+  userContentEdits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserContentEditConnection
+
+  """Can user react to this subject"""
+  viewerCanReact: Boolean!
+
+  """
+  Check if the viewer is able to change their subscription status for the repository.
+  """
+  viewerCanSubscribe: Boolean!
+
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+
+  """Reasons why the current viewer can not update this comment."""
+  viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+  """Did the viewer author this comment."""
+  viewerDidAuthor: Boolean!
+
+  """
+  Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+  """
+  viewerSubscription: SubscriptionState
+}
+
+"""Represents a comment on an Issue."""
+type IssueComment implements Node & Comment & Deletable & Updatable & UpdatableComment & Reactable & RepositoryNode {
+  """The actor who authored the comment."""
+  author: Actor
+
+  """Author's association with the subject of the comment."""
+  authorAssociation: CommentAuthorAssociation!
+
+  """The body as Markdown."""
+  body: String!
+
+  """The body rendered to HTML."""
+  bodyHTML: HTML!
+
+  """The body rendered to text."""
+  bodyText: String!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Check if this comment was created via an email reply."""
+  createdViaEmail: Boolean!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The actor who edited the comment."""
+  editor: Actor
+  id: ID!
+
+  """
+  Check if this comment was edited and includes an edit with the creation data
+  """
+  includesCreatedEdit: Boolean!
+
+  """Returns whether or not a comment has been minimized."""
+  isMinimized: Boolean!
+
+  """Identifies the issue associated with the comment."""
+  issue: Issue!
+
+  """The moment the editor made the last edit"""
+  lastEditedAt: DateTime
+
+  """Returns why the comment was minimized."""
+  minimizedReason: String
+
+  """Identifies when the comment was published at."""
+  publishedAt: DateTime
+
+  """
+  Returns the pull request associated with the comment, if this comment was made on a
+  pull request.
+
+  """
+  pullRequest: PullRequest
+
+  """A list of reactions grouped by content left on the subject."""
+  reactionGroups: [ReactionGroup!]
+
+  """A list of Reactions left on the Issue."""
+  reactions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Allows filtering Reactions by emoji."""
+    content: ReactionContent
+
+    """Allows specifying the order in which reactions are returned."""
+    orderBy: ReactionOrder
+  ): ReactionConnection!
+
+  """The repository associated with this node."""
+  repository: Repository!
+
+  """The HTTP path for this issue comment"""
+  resourcePath: URI!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this issue comment"""
+  url: URI!
+
+  """A list of edits to this content."""
+  userContentEdits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserContentEditConnection
+
+  """Check if the current viewer can delete this object."""
+  viewerCanDelete: Boolean!
+
+  """Check if the current viewer can minimize this object."""
+  viewerCanMinimize: Boolean!
+
+  """Can user react to this subject"""
+  viewerCanReact: Boolean!
+
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+
+  """Reasons why the current viewer can not update this comment."""
+  viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+  """Did the viewer author this comment."""
+  viewerDidAuthor: Boolean!
+}
+
+"""The connection type for IssueComment."""
+type IssueCommentConnection {
+  """A list of edges."""
+  edges: [IssueCommentEdge]
+
+  """A list of nodes."""
+  nodes: [IssueComment]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type IssueCommentEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: IssueComment
+}
+
+"""The connection type for Issue."""
+type IssueConnection {
+  """A list of edges."""
+  edges: [IssueEdge]
+
+  """A list of nodes."""
+  nodes: [Issue]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""This aggregates issues opened by a user within one repository."""
+type IssueContributionsByRepository {
+  """The issue contributions."""
+  contributions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Ordering options for contributions returned from the connection."""
+    orderBy: ContributionOrder
+  ): CreatedIssueContributionConnection!
+
+  """The repository in which the issues were opened."""
+  repository: Repository!
+}
+
+"""An edge in a connection."""
+type IssueEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Issue
+}
+
+"""Ways in which to filter lists of issues."""
+input IssueFilters {
+  """
+  List issues assigned to given name. Pass in `null` for issues with no assigned
+  user, and `*` for issues assigned to any user.
+  """
+  assignee: String
+
+  """List issues created by given name."""
+  createdBy: String
+
+  """List issues where the list of label names exist on the issue."""
+  labels: [String!]
+
+  """List issues where the given name is mentioned in the issue."""
+  mentioned: String
+
+  """
+  List issues by given milestone argument. If an string representation of an
+  integer is passed, it should refer to a milestone by its number field. Pass in
+  `null` for issues with no milestone, and `*` for issues that are assigned to any milestone.
+  """
+  milestone: String
+
+  """List issues that have been updated at or after the given date."""
+  since: DateTime
+
+  """List issues filtered by the list of states given."""
+  states: [IssueState!]
+
+  """List issues subscribed to by viewer."""
+  viewerSubscribed: Boolean = false
+}
+
+"""Ways in which lists of issues can be ordered upon return."""
+input IssueOrder {
+  """The field in which to order issues by."""
+  field: IssueOrderField!
+
+  """The direction in which to order issues by the specified field."""
+  direction: OrderDirection!
+}
+
+"""Properties by which issue connections can be ordered."""
+enum IssueOrderField {
+  """Order issues by creation time"""
+  CREATED_AT
+
+  """Order issues by update time"""
+  UPDATED_AT
+
+  """Order issues by comment count"""
+  COMMENTS
+}
+
+"""Used for return value of Repository.issueOrPullRequest."""
+union IssueOrPullRequest = Issue | PullRequest
+
+"""The possible PubSub channels for an issue."""
+enum IssuePubSubTopic {
+  """The channel ID for observing issue updates."""
+  UPDATED
+
+  """The channel ID for marking an issue as read."""
+  MARKASREAD
+
+  """The channel ID for updating items on the issue timeline."""
+  TIMELINE
+
+  """The channel ID for observing issue state updates."""
+  STATE
+}
+
+"""The possible states of an issue."""
+enum IssueState {
+  """An issue that is still open"""
+  OPEN
+
+  """An issue that has been closed"""
+  CLOSED
+}
+
+"""The connection type for IssueTimelineItem."""
+type IssueTimelineConnection {
+  """A list of edges."""
+  edges: [IssueTimelineItemEdge]
+
+  """A list of nodes."""
+  nodes: [IssueTimelineItem]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An item in an issue timeline"""
+union IssueTimelineItem = Commit | IssueComment | CrossReferencedEvent | ClosedEvent | ReopenedEvent | SubscribedEvent | UnsubscribedEvent | ReferencedEvent | AssignedEvent | UnassignedEvent | LabeledEvent | UnlabeledEvent | UserBlockedEvent | MilestonedEvent | DemilestonedEvent | RenamedTitleEvent | LockedEvent | UnlockedEvent | TransferredEvent
+
+"""An edge in a connection."""
+type IssueTimelineItemEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: IssueTimelineItem
+}
+
+"""An item in an issue timeline"""
+union IssueTimelineItems = IssueComment | CrossReferencedEvent | AddedToProjectEvent | AssignedEvent | ClosedEvent | CommentDeletedEvent | ConvertedNoteToIssueEvent | DemilestonedEvent | LabeledEvent | LockedEvent | MentionedEvent | MilestonedEvent | MovedColumnsInProjectEvent | PinnedEvent | ReferencedEvent | RemovedFromProjectEvent | RenamedTitleEvent | ReopenedEvent | SubscribedEvent | TransferredEvent | UnassignedEvent | UnlabeledEvent | UnlockedEvent | UserBlockedEvent | UnpinnedEvent | UnsubscribedEvent
+
+"""The connection type for IssueTimelineItems."""
+type IssueTimelineItemsConnection {
+  """A list of edges."""
+  edges: [IssueTimelineItemsEdge]
+
+  """
+  Identifies the count of items after applying `before` and `after` filters.
+  """
+  filteredCount: Int!
+
+  """A list of nodes."""
+  nodes: [IssueTimelineItems]
+
+  """
+  Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.
+  """
+  pageCount: Int!
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+
+  """Identifies the date and time when the timeline was last updated."""
+  updatedAt: DateTime!
+}
+
+"""An edge in a connection."""
+type IssueTimelineItemsEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: IssueTimelineItems
+}
+
+"""The possible item types found in a timeline."""
+enum IssueTimelineItemsItemType {
+  """Represents a comment on an Issue."""
+  ISSUE_COMMENT
+
+  """Represents a mention made by one issue or pull request to another."""
+  CROSS_REFERENCED_EVENT
+
+  """
+  Represents a 'added_to_project' event on a given issue or pull request.
+  """
+  ADDED_TO_PROJECT_EVENT
+
+  """Represents an 'assigned' event on any assignable object."""
+  ASSIGNED_EVENT
+
+  """Represents a 'closed' event on any `Closable`."""
+  CLOSED_EVENT
+
+  """Represents a 'comment_deleted' event on a given issue or pull request."""
+  COMMENT_DELETED_EVENT
+
+  """
+  Represents a 'converted_note_to_issue' event on a given issue or pull request.
+  """
+  CONVERTED_NOTE_TO_ISSUE_EVENT
+
+  """Represents a 'demilestoned' event on a given issue or pull request."""
+  DEMILESTONED_EVENT
+
+  """Represents a 'labeled' event on a given issue or pull request."""
+  LABELED_EVENT
+
+  """Represents a 'locked' event on a given issue or pull request."""
+  LOCKED_EVENT
+
+  """Represents a 'mentioned' event on a given issue or pull request."""
+  MENTIONED_EVENT
+
+  """Represents a 'milestoned' event on a given issue or pull request."""
+  MILESTONED_EVENT
+
+  """
+  Represents a 'moved_columns_in_project' event on a given issue or pull request.
+  """
+  MOVED_COLUMNS_IN_PROJECT_EVENT
+
+  """Represents a 'pinned' event on a given issue or pull request."""
+  PINNED_EVENT
+
+  """Represents a 'referenced' event on a given `ReferencedSubject`."""
+  REFERENCED_EVENT
+
+  """
+  Represents a 'removed_from_project' event on a given issue or pull request.
+  """
+  REMOVED_FROM_PROJECT_EVENT
+
+  """Represents a 'renamed' event on a given issue or pull request"""
+  RENAMED_TITLE_EVENT
+
+  """Represents a 'reopened' event on any `Closable`."""
+  REOPENED_EVENT
+
+  """Represents a 'subscribed' event on a given `Subscribable`."""
+  SUBSCRIBED_EVENT
+
+  """Represents a 'transferred' event on a given issue or pull request."""
+  TRANSFERRED_EVENT
+
+  """Represents an 'unassigned' event on any assignable object."""
+  UNASSIGNED_EVENT
+
+  """Represents an 'unlabeled' event on a given issue or pull request."""
+  UNLABELED_EVENT
+
+  """Represents an 'unlocked' event on a given issue or pull request."""
+  UNLOCKED_EVENT
+
+  """Represents a 'user_blocked' event on a given user."""
+  USER_BLOCKED_EVENT
+
+  """Represents an 'unpinned' event on a given issue or pull request."""
+  UNPINNED_EVENT
+
+  """Represents an 'unsubscribed' event on a given `Subscribable`."""
+  UNSUBSCRIBED_EVENT
+}
+
+"""Represents a user signing up for a GitHub account."""
+type JoinedGitHubContribution implements Contribution {
+  """
+  Whether this contribution is associated with a record you do not have access to. For
+  example, your own 'first issue' contribution may have been made on a repository you can no
+  longer access.
+
+  """
+  isRestricted: Boolean!
+
+  """When this contribution was made."""
+  occurredAt: DateTime!
+
+  """The HTTP path for this contribution."""
+  resourcePath: URI!
+
+  """The HTTP URL for this contribution."""
+  url: URI!
+
+  """
+  The user who made this contribution.
+
+  """
+  user: User!
+}
+
+"""A label for categorizing Issues or Milestones with a given Repository."""
+type Label implements Node {
+  """Identifies the label color."""
+  color: String!
+
+  """Identifies the date and time when the label was created."""
+  createdAt: DateTime
+
+  """A brief description of this label."""
+  description: String
+  id: ID!
+
+  """Indicates whether or not this is a default label."""
+  isDefault: Boolean!
+
+  """A list of issues associated with this label."""
+  issues(
+    """Ordering options for issues returned from the connection."""
+    orderBy: IssueOrder
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """A list of states to filter the issues by."""
+    states: [IssueState!]
+
+    """Filtering options for issues returned from the connection."""
+    filterBy: IssueFilters
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueConnection!
+
+  """Identifies the label name."""
+  name: String!
+
+  """A list of pull requests associated with this label."""
+  pullRequests(
+    """A list of states to filter the pull requests by."""
+    states: [PullRequestState!]
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """The head ref name to filter the pull requests by."""
+    headRefName: String
+
+    """The base ref name to filter the pull requests by."""
+    baseRefName: String
+
+    """Ordering options for pull requests returned from the connection."""
+    orderBy: IssueOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestConnection!
+
+  """The repository associated with this label."""
+  repository: Repository!
+
+  """The HTTP path for this label."""
+  resourcePath: URI!
+
+  """Identifies the date and time when the label was last updated."""
+  updatedAt: DateTime
+
+  """The HTTP URL for this label."""
+  url: URI!
+}
+
+"""An object that can have labels assigned to it."""
+interface Labelable {
+  """A list of labels associated with the object."""
+  labels(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): LabelConnection
+}
+
+"""The connection type for Label."""
+type LabelConnection {
+  """A list of edges."""
+  edges: [LabelEdge]
+
+  """A list of nodes."""
+  nodes: [Label]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a 'labeled' event on a given issue or pull request."""
+type LabeledEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Identifies the label associated with the 'labeled' event."""
+  label: Label!
+
+  """Identifies the `Labelable` associated with the event."""
+  labelable: Labelable!
+}
+
+"""An edge in a connection."""
+type LabelEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Label
+}
+
+"""Represents a given language found in repositories."""
+type Language implements Node {
+  """The color defined for the current language."""
+  color: String
+  id: ID!
+
+  """The name of the current language."""
+  name: String!
+}
+
+"""A list of languages associated with the parent."""
+type LanguageConnection {
+  """A list of edges."""
+  edges: [LanguageEdge]
+
+  """A list of nodes."""
+  nodes: [Language]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+
+  """The total size in bytes of files written in that language."""
+  totalSize: Int!
+}
+
+"""Represents the language of a repository."""
+type LanguageEdge {
+  cursor: String!
+  node: Language!
+
+  """The number of bytes of code written in the language."""
+  size: Int!
+}
+
+"""Ordering options for language connections."""
+input LanguageOrder {
+  """The field to order languages by."""
+  field: LanguageOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which language connections can be ordered."""
+enum LanguageOrderField {
+  """Order languages by the size of all files containing the language"""
+  SIZE
+}
+
+"""A repository's open source license"""
+type License implements Node {
+  """The full text of the license"""
+  body: String!
+
+  """The conditions set by the license"""
+  conditions: [LicenseRule]!
+
+  """A human-readable description of the license"""
+  description: String
+
+  """Whether the license should be featured"""
+  featured: Boolean!
+
+  """Whether the license should be displayed in license pickers"""
+  hidden: Boolean!
+  id: ID!
+
+  """Instructions on how to implement the license"""
+  implementation: String
+
+  """The lowercased SPDX ID of the license"""
+  key: String!
+
+  """The limitations set by the license"""
+  limitations: [LicenseRule]!
+
+  """The license full name specified by <https://spdx.org/licenses>"""
+  name: String!
+
+  """Customary short name if applicable (e.g, GPLv3)"""
+  nickname: String
+
+  """The permissions set by the license"""
+  permissions: [LicenseRule]!
+
+  """
+  Whether the license is a pseudo-license placeholder (e.g., other, no-license)
+  """
+  pseudoLicense: Boolean!
+
+  """Short identifier specified by <https://spdx.org/licenses>"""
+  spdxId: String
+
+  """URL to the license on <https://choosealicense.com>"""
+  url: URI
+}
+
+"""Describes a License's conditions, permissions, and limitations"""
+type LicenseRule {
+  """A description of the rule"""
+  description: String!
+
+  """The machine-readable rule key"""
+  key: String!
+
+  """The human-readable rule label"""
+  label: String!
+}
+
+"""An object that can be locked."""
+interface Lockable {
+  """Reason that the conversation was locked."""
+  activeLockReason: LockReason
+
+  """`true` if the object is locked"""
+  locked: Boolean!
+}
+
+"""Represents a 'locked' event on a given issue or pull request."""
+type LockedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Reason that the conversation was locked (optional)."""
+  lockReason: LockReason
+
+  """Object that was locked."""
+  lockable: Lockable!
+}
+
+"""Autogenerated input type of LockLockable"""
+input LockLockableInput {
+  """ID of the issue or pull request to be locked."""
+  lockableId: ID!
+
+  """A reason for why the issue or pull request will be locked."""
+  lockReason: LockReason
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of LockLockable"""
+type LockLockablePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The item that was locked."""
+  lockedRecord: Lockable
+}
+
+"""The possible reasons that an issue or pull request was locked."""
+enum LockReason {
+  """
+  The issue or pull request was locked because the conversation was off-topic.
+  """
+  OFF_TOPIC
+
+  """
+  The issue or pull request was locked because the conversation was too heated.
+  """
+  TOO_HEATED
+
+  """
+  The issue or pull request was locked because the conversation was resolved.
+  """
+  RESOLVED
+
+  """
+  The issue or pull request was locked because the conversation was spam.
+  """
+  SPAM
+}
+
+"""A placeholder user for attribution of imported data on GitHub."""
+type Mannequin implements Node & Actor & UniformResourceLocatable {
+  """A URL pointing to the GitHub App's public avatar."""
+  avatarUrl(
+    """The size of the resulting square image."""
+    size: Int
+  ): URI!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+
+  """The username of the actor."""
+  login: String!
+
+  """The HTML path to this resource."""
+  resourcePath: URI!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The URL to this resource."""
+  url: URI!
+}
+
+"""A public description of a Marketplace category."""
+type MarketplaceCategory implements Node {
+  """The category's description."""
+  description: String
+
+  """
+  The technical description of how apps listed in this category work with GitHub.
+  """
+  howItWorks: String
+  id: ID!
+
+  """The category's name."""
+  name: String!
+
+  """How many Marketplace listings have this as their primary category."""
+  primaryListingCount: Int!
+
+  """The HTTP path for this Marketplace category."""
+  resourcePath: URI!
+
+  """How many Marketplace listings have this as their secondary category."""
+  secondaryListingCount: Int!
+
+  """The short name of the category used in its URL."""
+  slug: String!
+
+  """The HTTP URL for this Marketplace category."""
+  url: URI!
+}
+
+"""A listing in the GitHub integration marketplace."""
+type MarketplaceListing implements Node {
+  """The GitHub App this listing represents."""
+  app: App
+
+  """URL to the listing owner's company site."""
+  companyUrl: URI
+
+  """
+  The HTTP path for configuring access to the listing's integration or OAuth app
+  """
+  configurationResourcePath: URI!
+
+  """
+  The HTTP URL for configuring access to the listing's integration or OAuth app
+  """
+  configurationUrl: URI!
+
+  """URL to the listing's documentation."""
+  documentationUrl: URI
+
+  """The listing's detailed description."""
+  extendedDescription: String
+
+  """The listing's detailed description rendered to HTML."""
+  extendedDescriptionHTML: HTML!
+
+  """The listing's introductory description."""
+  fullDescription: String!
+
+  """The listing's introductory description rendered to HTML."""
+  fullDescriptionHTML: HTML!
+
+  """
+  Whether this listing has been submitted for review from GitHub for approval to be displayed in the Marketplace.
+  """
+  hasApprovalBeenRequested: Boolean! @deprecated(reason: "`hasApprovalBeenRequested` will be removed. Use `isVerificationPendingFromDraft` instead. Removal on 2019-10-01 UTC.")
+
+  """Does this listing have any plans with a free trial?"""
+  hasPublishedFreeTrialPlans: Boolean!
+
+  """Does this listing have a terms of service link?"""
+  hasTermsOfService: Boolean!
+
+  """A technical description of how this app works with GitHub."""
+  howItWorks: String
+
+  """The listing's technical description rendered to HTML."""
+  howItWorksHTML: HTML!
+  id: ID!
+
+  """URL to install the product to the viewer's account or organization."""
+  installationUrl: URI
+
+  """Whether this listing's app has been installed for the current viewer"""
+  installedForViewer: Boolean!
+
+  """Whether this listing has been approved for display in the Marketplace."""
+  isApproved: Boolean! @deprecated(reason: "`isApproved` will be removed. Use `isPublic` instead. Removal on 2019-10-01 UTC.")
+
+  """Whether this listing has been removed from the Marketplace."""
+  isArchived: Boolean!
+
+  """Whether this listing has been removed from the Marketplace."""
+  isDelisted: Boolean! @deprecated(reason: "`isDelisted` will be removed. Use `isArchived` instead. Removal on 2019-10-01 UTC.")
+
+  """
+  Whether this listing is still an editable draft that has not been submitted
+  for review and is not publicly visible in the Marketplace.
+  """
+  isDraft: Boolean!
+
+  """
+  Whether the product this listing represents is available as part of a paid plan.
+  """
+  isPaid: Boolean!
+
+  """Whether this listing has been approved for display in the Marketplace."""
+  isPublic: Boolean!
+
+  """
+  Whether this listing has been rejected by GitHub for display in the Marketplace.
+  """
+  isRejected: Boolean!
+
+  """
+  Whether this listing has been approved for unverified display in the Marketplace.
+  """
+  isUnverified: Boolean!
+
+  """
+  Whether this draft listing has been submitted for review for approval to be unverified in the Marketplace.
+  """
+  isUnverifiedPending: Boolean!
+
+  """
+  Whether this draft listing has been submitted for review from GitHub for approval to be verified in the Marketplace.
+  """
+  isVerificationPendingFromDraft: Boolean!
+
+  """
+  Whether this unverified listing has been submitted for review from GitHub for approval to be verified in the Marketplace.
+  """
+  isVerificationPendingFromUnverified: Boolean!
+
+  """
+  Whether this listing has been approved for verified display in the Marketplace.
+  """
+  isVerified: Boolean!
+
+  """The hex color code, without the leading '#', for the logo background."""
+  logoBackgroundColor: String!
+
+  """URL for the listing's logo image."""
+  logoUrl(
+    """The size in pixels of the resulting square image."""
+    size: Int = 400
+  ): URI
+
+  """The listing's full name."""
+  name: String!
+
+  """
+  The listing's very short description without a trailing period or ampersands.
+  """
+  normalizedShortDescription: String!
+
+  """URL to the listing's detailed pricing."""
+  pricingUrl: URI
+
+  """The category that best describes the listing."""
+  primaryCategory: MarketplaceCategory!
+
+  """
+  URL to the listing's privacy policy, may return an empty string for listings that do not require a privacy policy URL.
+  """
+  privacyPolicyUrl: URI!
+
+  """The HTTP path for the Marketplace listing."""
+  resourcePath: URI!
+
+  """The URLs for the listing's screenshots."""
+  screenshotUrls: [String]!
+
+  """An alternate category that describes the listing."""
+  secondaryCategory: MarketplaceCategory
+
+  """The listing's very short description."""
+  shortDescription: String!
+
+  """The short name of the listing used in its URL."""
+  slug: String!
+
+  """URL to the listing's status page."""
+  statusUrl: URI
+
+  """An email address for support for this listing's app."""
+  supportEmail: String
+
+  """
+  Either a URL or an email address for support for this listing's app, may
+  return an empty string for listings that do not require a support URL.
+  """
+  supportUrl: URI!
+
+  """URL to the listing's terms of service."""
+  termsOfServiceUrl: URI
+
+  """The HTTP URL for the Marketplace listing."""
+  url: URI!
+
+  """Can the current viewer add plans for this Marketplace listing."""
+  viewerCanAddPlans: Boolean!
+
+  """Can the current viewer approve this Marketplace listing."""
+  viewerCanApprove: Boolean!
+
+  """Can the current viewer delist this Marketplace listing."""
+  viewerCanDelist: Boolean!
+
+  """Can the current viewer edit this Marketplace listing."""
+  viewerCanEdit: Boolean!
+
+  """
+  Can the current viewer edit the primary and secondary category of this
+  Marketplace listing.
+
+  """
+  viewerCanEditCategories: Boolean!
+
+  """Can the current viewer edit the plans for this Marketplace listing."""
+  viewerCanEditPlans: Boolean!
+
+  """
+  Can the current viewer return this Marketplace listing to draft state
+  so it becomes editable again.
+
+  """
+  viewerCanRedraft: Boolean!
+
+  """
+  Can the current viewer reject this Marketplace listing by returning it to
+  an editable draft state or rejecting it entirely.
+
+  """
+  viewerCanReject: Boolean!
+
+  """
+  Can the current viewer request this listing be reviewed for display in
+  the Marketplace as verified.
+
+  """
+  viewerCanRequestApproval: Boolean!
+
+  """
+  Indicates whether the current user has an active subscription to this Marketplace listing.
+
+  """
+  viewerHasPurchased: Boolean!
+
+  """
+  Indicates if the current user has purchased a subscription to this Marketplace listing
+  for all of the organizations the user owns.
+
+  """
+  viewerHasPurchasedForAllOrganizations: Boolean!
+
+  """
+  Does the current viewer role allow them to administer this Marketplace listing.
+
+  """
+  viewerIsListingAdmin: Boolean!
+}
+
+"""Look up Marketplace Listings"""
+type MarketplaceListingConnection {
+  """A list of edges."""
+  edges: [MarketplaceListingEdge]
+
+  """A list of nodes."""
+  nodes: [MarketplaceListing]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type MarketplaceListingEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: MarketplaceListing
+}
+
+"""Entities that have members who can set status messages."""
+interface MemberStatusable {
+  """
+  Get the status messages members of this entity have set that are either public or visible only to the organization.
+  """
+  memberStatuses(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Ordering options for user statuses returned from the connection."""
+    orderBy: UserStatusOrder
+  ): UserStatusConnection!
+}
+
+"""Represents a 'mentioned' event on a given issue or pull request."""
+type MentionedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+}
+
+"""Whether or not a PullRequest can be merged."""
+enum MergeableState {
+  """The pull request can be merged."""
+  MERGEABLE
+
+  """The pull request cannot be merged due to merge conflicts."""
+  CONFLICTING
+
+  """The mergeability of the pull request is still being calculated."""
+  UNKNOWN
+}
+
+"""Represents a 'merged' event on a given pull request."""
+type MergedEvent implements Node & UniformResourceLocatable {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the commit associated with the `merge` event."""
+  commit: Commit
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Identifies the Ref associated with the `merge` event."""
+  mergeRef: Ref
+
+  """Identifies the name of the Ref associated with the `merge` event."""
+  mergeRefName: String!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+
+  """The HTTP path for this merged event."""
+  resourcePath: URI!
+
+  """The HTTP URL for this merged event."""
+  url: URI!
+}
+
+"""Autogenerated input type of MergePullRequest"""
+input MergePullRequestInput {
+  """ID of the pull request to be merged."""
+  pullRequestId: ID!
+
+  """
+  Commit headline to use for the merge commit; if omitted, a default message will be used.
+  """
+  commitHeadline: String
+
+  """
+  Commit body to use for the merge commit; if omitted, a default message will be used
+  """
+  commitBody: String
+
+  """
+  OID that the pull request head ref must match to allow merge; if omitted, no check is performed.
+  """
+  expectedHeadOid: GitObjectID
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of MergePullRequest"""
+type MergePullRequestPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The pull request that was merged."""
+  pullRequest: PullRequest
+}
+
+"""Represents a Milestone object on a given repository."""
+type Milestone implements Node & Closable & UniformResourceLocatable {
+  """
+  `true` if the object is closed (definition of closed may depend on type)
+  """
+  closed: Boolean!
+
+  """Identifies the date and time when the object was closed."""
+  closedAt: DateTime
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the actor who created the milestone."""
+  creator: Actor
+
+  """Identifies the description of the milestone."""
+  description: String
+
+  """Identifies the due date of the milestone."""
+  dueOn: DateTime
+  id: ID!
+
+  """A list of issues associated with the milestone."""
+  issues(
+    """Ordering options for issues returned from the connection."""
+    orderBy: IssueOrder
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """A list of states to filter the issues by."""
+    states: [IssueState!]
+
+    """Filtering options for issues returned from the connection."""
+    filterBy: IssueFilters
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueConnection!
+
+  """Identifies the number of the milestone."""
+  number: Int!
+
+  """A list of pull requests associated with the milestone."""
+  pullRequests(
+    """A list of states to filter the pull requests by."""
+    states: [PullRequestState!]
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """The head ref name to filter the pull requests by."""
+    headRefName: String
+
+    """The base ref name to filter the pull requests by."""
+    baseRefName: String
+
+    """Ordering options for pull requests returned from the connection."""
+    orderBy: IssueOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestConnection!
+
+  """The repository associated with this milestone."""
+  repository: Repository!
+
+  """The HTTP path for this milestone"""
+  resourcePath: URI!
+
+  """Identifies the state of the milestone."""
+  state: MilestoneState!
+
+  """Identifies the title of the milestone."""
+  title: String!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this milestone"""
+  url: URI!
+}
+
+"""The connection type for Milestone."""
+type MilestoneConnection {
+  """A list of edges."""
+  edges: [MilestoneEdge]
+
+  """A list of nodes."""
+  nodes: [Milestone]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a 'milestoned' event on a given issue or pull request."""
+type MilestonedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Identifies the milestone title associated with the 'milestoned' event."""
+  milestoneTitle: String!
+
+  """Object referenced by event."""
+  subject: MilestoneItem!
+}
+
+"""An edge in a connection."""
+type MilestoneEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Milestone
+}
+
+"""Types that can be inside a Milestone."""
+union MilestoneItem = Issue | PullRequest
+
+"""Ordering options for milestone connections."""
+input MilestoneOrder {
+  """The field to order milestones by."""
+  field: MilestoneOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which milestone connections can be ordered."""
+enum MilestoneOrderField {
+  """Order milestones by when they are due."""
+  DUE_DATE
+
+  """Order milestones by when they were created."""
+  CREATED_AT
+
+  """Order milestones by when they were last updated."""
+  UPDATED_AT
+
+  """Order milestones by their number."""
+  NUMBER
+}
+
+"""The possible states of a milestone."""
+enum MilestoneState {
+  """A milestone that is still open."""
+  OPEN
+
+  """A milestone that has been closed."""
+  CLOSED
+}
+
+"""Autogenerated input type of MinimizeComment"""
+input MinimizeCommentInput {
+  """The Node ID of the subject to modify."""
+  subjectId: ID!
+
+  """The classification of comment"""
+  classifier: ReportedContentClassifiers!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""
+Represents a 'moved_columns_in_project' event on a given issue or pull request.
+"""
+type MovedColumnsInProjectEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+}
+
+"""Autogenerated input type of MoveProjectCard"""
+input MoveProjectCardInput {
+  """The id of the card to move."""
+  cardId: ID!
+
+  """The id of the column to move it into."""
+  columnId: ID!
+
+  """
+  Place the new card after the card with this id. Pass null to place it at the top.
+  """
+  afterCardId: ID
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of MoveProjectCard"""
+type MoveProjectCardPayload {
+  """The new edge of the moved card."""
+  cardEdge: ProjectCardEdge
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated input type of MoveProjectColumn"""
+input MoveProjectColumnInput {
+  """The id of the column to move."""
+  columnId: ID!
+
+  """
+  Place the new column after the column with this id. Pass null to place it at the front.
+  """
+  afterColumnId: ID
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of MoveProjectColumn"""
+type MoveProjectColumnPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The new edge of the moved column."""
+  columnEdge: ProjectColumnEdge
+}
+
+"""The root query for implementing GraphQL mutations."""
+type Mutation {
+  """Applies a suggested topic to the repository."""
+  acceptTopicSuggestion(input: AcceptTopicSuggestionInput!): AcceptTopicSuggestionPayload
+
+  """Adds assignees to an assignable object."""
+  addAssigneesToAssignable(input: AddAssigneesToAssignableInput!): AddAssigneesToAssignablePayload
+
+  """Adds a comment to an Issue or Pull Request."""
+  addComment(input: AddCommentInput!): AddCommentPayload
+
+  """Adds labels to a labelable object."""
+  addLabelsToLabelable(input: AddLabelsToLabelableInput!): AddLabelsToLabelablePayload
+
+  """
+  Adds a card to a ProjectColumn. Either `contentId` or `note` must be provided but **not** both.
+  """
+  addProjectCard(input: AddProjectCardInput!): AddProjectCardPayload
+
+  """Adds a column to a Project."""
+  addProjectColumn(input: AddProjectColumnInput!): AddProjectColumnPayload
+
+  """Adds a review to a Pull Request."""
+  addPullRequestReview(input: AddPullRequestReviewInput!): AddPullRequestReviewPayload
+
+  """Adds a comment to a review."""
+  addPullRequestReviewComment(input: AddPullRequestReviewCommentInput!): AddPullRequestReviewCommentPayload
+
+  """Adds a reaction to a subject."""
+  addReaction(input: AddReactionInput!): AddReactionPayload
+
+  """Adds a star to a Starrable."""
+  addStar(input: AddStarInput!): AddStarPayload
+
+  """Update your status on GitHub."""
+  changeUserStatus(input: ChangeUserStatusInput!): ChangeUserStatusPayload
+
+  """Clears all labels from a labelable object."""
+  clearLabelsFromLabelable(input: ClearLabelsFromLabelableInput!): ClearLabelsFromLabelablePayload
+
+  """
+  Creates a new project by cloning configuration from an existing project.
+  """
+  cloneProject(input: CloneProjectInput!): CloneProjectPayload
+
+  """Close an issue."""
+  closeIssue(input: CloseIssueInput!): CloseIssuePayload
+
+  """Close a pull request."""
+  closePullRequest(input: ClosePullRequestInput!): ClosePullRequestPayload
+
+  """
+  Convert a project note card to one associated with a newly created issue.
+  """
+  convertProjectCardNoteToIssue(input: ConvertProjectCardNoteToIssueInput!): ConvertProjectCardNoteToIssuePayload
+
+  """Create a new branch protection rule"""
+  createBranchProtectionRule(input: CreateBranchProtectionRuleInput!): CreateBranchProtectionRulePayload
+
+  """Creates a new issue."""
+  createIssue(input: CreateIssueInput!): CreateIssuePayload
+
+  """Creates a new project."""
+  createProject(input: CreateProjectInput!): CreateProjectPayload
+
+  """Create a new pull request"""
+  createPullRequest(input: CreatePullRequestInput!): CreatePullRequestPayload
+
+  """Rejects a suggested topic for the repository."""
+  declineTopicSuggestion(input: DeclineTopicSuggestionInput!): DeclineTopicSuggestionPayload
+
+  """Delete a branch protection rule"""
+  deleteBranchProtectionRule(input: DeleteBranchProtectionRuleInput!): DeleteBranchProtectionRulePayload
+
+  """Deletes an Issue object."""
+  deleteIssue(input: DeleteIssueInput!): DeleteIssuePayload
+
+  """Deletes an IssueComment object."""
+  deleteIssueComment(input: DeleteIssueCommentInput!): DeleteIssueCommentPayload
+
+  """Deletes a project."""
+  deleteProject(input: DeleteProjectInput!): DeleteProjectPayload
+
+  """Deletes a project card."""
+  deleteProjectCard(input: DeleteProjectCardInput!): DeleteProjectCardPayload
+
+  """Deletes a project column."""
+  deleteProjectColumn(input: DeleteProjectColumnInput!): DeleteProjectColumnPayload
+
+  """Deletes a pull request review."""
+  deletePullRequestReview(input: DeletePullRequestReviewInput!): DeletePullRequestReviewPayload
+
+  """Deletes a pull request review comment."""
+  deletePullRequestReviewComment(input: DeletePullRequestReviewCommentInput!): DeletePullRequestReviewCommentPayload
+
+  """Dismisses an approved or rejected pull request review."""
+  dismissPullRequestReview(input: DismissPullRequestReviewInput!): DismissPullRequestReviewPayload
+
+  """Lock a lockable object"""
+  lockLockable(input: LockLockableInput!): LockLockablePayload
+
+  """Merge a pull request."""
+  mergePullRequest(input: MergePullRequestInput!): MergePullRequestPayload
+
+  """Moves a project card to another place."""
+  moveProjectCard(input: MoveProjectCardInput!): MoveProjectCardPayload
+
+  """Moves a project column to another place."""
+  moveProjectColumn(input: MoveProjectColumnInput!): MoveProjectColumnPayload
+
+  """Removes assignees from an assignable object."""
+  removeAssigneesFromAssignable(input: RemoveAssigneesFromAssignableInput!): RemoveAssigneesFromAssignablePayload
+
+  """Removes labels from a Labelable object."""
+  removeLabelsFromLabelable(input: RemoveLabelsFromLabelableInput!): RemoveLabelsFromLabelablePayload
+
+  """Removes outside collaborator from all repositories in an organization."""
+  removeOutsideCollaborator(input: RemoveOutsideCollaboratorInput!): RemoveOutsideCollaboratorPayload
+
+  """Removes a reaction from a subject."""
+  removeReaction(input: RemoveReactionInput!): RemoveReactionPayload
+
+  """Removes a star from a Starrable."""
+  removeStar(input: RemoveStarInput!): RemoveStarPayload
+
+  """Reopen a issue."""
+  reopenIssue(input: ReopenIssueInput!): ReopenIssuePayload
+
+  """Reopen a pull request."""
+  reopenPullRequest(input: ReopenPullRequestInput!): ReopenPullRequestPayload
+
+  """Set review requests on a pull request."""
+  requestReviews(input: RequestReviewsInput!): RequestReviewsPayload
+
+  """Marks a review thread as resolved."""
+  resolveReviewThread(input: ResolveReviewThreadInput!): ResolveReviewThreadPayload
+
+  """Submits a pending pull request review."""
+  submitPullRequestReview(input: SubmitPullRequestReviewInput!): SubmitPullRequestReviewPayload
+
+  """Unlock a lockable object"""
+  unlockLockable(input: UnlockLockableInput!): UnlockLockablePayload
+
+  """Unmark an issue as a duplicate of another issue."""
+  unmarkIssueAsDuplicate(input: UnmarkIssueAsDuplicateInput!): UnmarkIssueAsDuplicatePayload
+
+  """Marks a review thread as unresolved."""
+  unresolveReviewThread(input: UnresolveReviewThreadInput!): UnresolveReviewThreadPayload
+
+  """Create a new branch protection rule"""
+  updateBranchProtectionRule(input: UpdateBranchProtectionRuleInput!): UpdateBranchProtectionRulePayload
+
+  """Updates an Issue."""
+  updateIssue(input: UpdateIssueInput!): UpdateIssuePayload
+
+  """Updates an IssueComment object."""
+  updateIssueComment(input: UpdateIssueCommentInput!): UpdateIssueCommentPayload
+
+  """Updates an existing project."""
+  updateProject(input: UpdateProjectInput!): UpdateProjectPayload
+
+  """Updates an existing project card."""
+  updateProjectCard(input: UpdateProjectCardInput!): UpdateProjectCardPayload
+
+  """Updates an existing project column."""
+  updateProjectColumn(input: UpdateProjectColumnInput!): UpdateProjectColumnPayload
+
+  """Update a pull request"""
+  updatePullRequest(input: UpdatePullRequestInput!): UpdatePullRequestPayload
+
+  """Updates the body of a pull request review."""
+  updatePullRequestReview(input: UpdatePullRequestReviewInput!): UpdatePullRequestReviewPayload
+
+  """Updates a pull request review comment."""
+  updatePullRequestReviewComment(input: UpdatePullRequestReviewCommentInput!): UpdatePullRequestReviewCommentPayload
+
+  """Updates the state for subscribable subjects."""
+  updateSubscription(input: UpdateSubscriptionInput!): UpdateSubscriptionPayload
+
+  """Replaces the repository's topics with the given topics."""
+  updateTopics(input: UpdateTopicsInput!): UpdateTopicsPayload
+}
+
+"""An object with an ID."""
+interface Node {
+  """ID of the object."""
+  id: ID!
+}
+
+"""
+Possible directions in which to order a list of items when provided an `orderBy` argument.
+"""
+enum OrderDirection {
+  """Specifies an ascending order for a given `orderBy` argument."""
+  ASC
+
+  """Specifies a descending order for a given `orderBy` argument."""
+  DESC
+}
+
+"""
+An account on GitHub, with one or more owners, that has repositories, members and teams.
+"""
+type Organization implements Node & Actor & RegistryPackageOwner & RegistryPackageSearch & ProjectOwner & RepositoryOwner & UniformResourceLocatable & MemberStatusable & ProfileOwner {
+  """
+  Determine if this repository owner has any items that can be pinned to their profile.
+  """
+  anyPinnableItems(
+    """Filter to only a particular kind of pinnable item."""
+    type: PinnableItemType
+  ): Boolean!
+
+  """A URL pointing to the organization's public avatar."""
+  avatarUrl(
+    """The size of the resulting square image."""
+    size: Int
+  ): URI!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The organization's public profile description."""
+  description: String
+
+  """The organization's public email."""
+  email: String
+  id: ID!
+
+  """Whether the organization has verified its profile email and website."""
+  isVerified: Boolean!
+
+  """
+  Showcases a selection of repositories and gists that the profile owner has
+  either curated or that have been selected automatically based on popularity.
+  """
+  itemShowcase: ProfileItemShowcase!
+
+  """The organization's public profile location."""
+  location: String
+
+  """The organization's login name."""
+  login: String!
+
+  """
+  Get the status messages members of this entity have set that are either public or visible only to the organization.
+  """
+  memberStatuses(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Ordering options for user statuses returned from the connection."""
+    orderBy: UserStatusOrder
+  ): UserStatusConnection!
+
+  """A list of users who are members of this organization."""
+  membersWithRole(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): OrganizationMemberConnection!
+
+  """The organization's public profile name."""
+  name: String
+
+  """The HTTP path creating a new team"""
+  newTeamResourcePath: URI!
+
+  """The HTTP URL creating a new team"""
+  newTeamUrl: URI!
+
+  """The billing email for the organization."""
+  organizationBillingEmail: String
+
+  """A list of users who have been invited to join this organization."""
+  pendingMembers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+
+  """
+  A list of repositories and gists this profile owner can pin to their profile.
+  """
+  pinnableItems(
+    """Filter the types of pinnable items that are returned."""
+    types: [PinnableItemType!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PinnableItemConnection!
+
+  """
+  A list of repositories and gists this profile owner has pinned to their profile
+  """
+  pinnedItems(
+    """Filter the types of pinned items that are returned."""
+    types: [PinnableItemType!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PinnableItemConnection!
+
+  """
+  Returns how many more items this profile owner can pin to their profile.
+  """
+  pinnedItemsRemaining: Int!
+
+  """A list of repositories this user has pinned to their profile"""
+  pinnedRepositories(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """
+    Array of viewer's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    current viewer owns.
+    """
+    affiliations: [RepositoryAffiliation]
+
+    """
+    Array of owner's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    organization or user being viewed owns.
+    """
+    ownerAffiliations: [RepositoryAffiliation]
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RepositoryConnection! @deprecated(reason: "pinnedRepositories will be removed Use ProfileOwner.pinnedItems instead. Removal on 2019-07-01 UTC.")
+
+  """Find project by number."""
+  project(
+    """The project number to find."""
+    number: Int!
+  ): Project
+
+  """A list of projects under the owner."""
+  projects(
+    """Ordering options for projects returned from the connection"""
+    orderBy: ProjectOrder
+
+    """Query to search projects by, currently only searching by name."""
+    search: String
+
+    """A list of states to filter the projects by."""
+    states: [ProjectState!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ProjectConnection!
+
+  """The HTTP path listing organization's projects"""
+  projectsResourcePath: URI!
+
+  """The HTTP URL listing organization's projects"""
+  projectsUrl: URI!
+
+  """A list of repositories that the user owns."""
+  repositories(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """
+    Array of viewer's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    current viewer owns.
+    """
+    affiliations: [RepositoryAffiliation]
+
+    """
+    Array of owner's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    organization or user being viewed owns.
+    """
+    ownerAffiliations: [RepositoryAffiliation]
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """
+    If non-null, filters repositories according to whether they are forks of another repository
+    """
+    isFork: Boolean
+  ): RepositoryConnection!
+
+  """Find Repository."""
+  repository(
+    """Name of Repository to find."""
+    name: String!
+  ): Repository
+
+  """
+  When true the organization requires all members, billing managers, and outside
+  collaborators to enable two-factor authentication.
+  """
+  requiresTwoFactorAuthentication: Boolean
+
+  """The HTTP path for this organization."""
+  resourcePath: URI!
+
+  """The Organization's SAML identity providers"""
+  samlIdentityProvider: OrganizationIdentityProvider
+
+  """Find an organization's team by its slug."""
+  team(
+    """The name or slug of the team to find."""
+    slug: String!
+  ): Team
+
+  """A list of teams in this organization."""
+  teams(
+    """If non-null, filters teams according to privacy"""
+    privacy: TeamPrivacy
+
+    """
+    If non-null, filters teams according to whether the viewer is an admin or member on team
+    """
+    role: TeamRole
+
+    """If non-null, filters teams with query on team name and team slug"""
+    query: String
+
+    """User logins to filter by"""
+    userLogins: [String!]
+
+    """Ordering options for teams returned from the connection"""
+    orderBy: TeamOrder
+
+    """
+    If true, filters teams that are mapped to an LDAP Group (Enterprise only)
+    """
+    ldapMapped: Boolean
+
+    """If true, restrict to only root teams"""
+    rootTeamsOnly: Boolean = false
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): TeamConnection!
+
+  """The HTTP path listing organization's teams"""
+  teamsResourcePath: URI!
+
+  """The HTTP URL listing organization's teams"""
+  teamsUrl: URI!
+
+  """The HTTP URL for this organization."""
+  url: URI!
+
+  """Organization is adminable by the viewer."""
+  viewerCanAdminister: Boolean!
+
+  """Can the viewer pin repositories and gists to the profile?"""
+  viewerCanChangePinnedItems: Boolean!
+
+  """Can the current viewer create new projects on this owner."""
+  viewerCanCreateProjects: Boolean!
+
+  """Viewer can create repositories on this organization"""
+  viewerCanCreateRepositories: Boolean!
+
+  """Viewer can create teams on this organization."""
+  viewerCanCreateTeams: Boolean!
+
+  """Viewer is an active member of this organization."""
+  viewerIsAMember: Boolean!
+
+  """The organization's public profile URL."""
+  websiteUrl: URI
+}
+
+"""The connection type for Organization."""
+type OrganizationConnection {
+  """A list of edges."""
+  edges: [OrganizationEdge]
+
+  """A list of nodes."""
+  nodes: [Organization]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type OrganizationEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Organization
+}
+
+"""
+An Identity Provider configured to provision SAML and SCIM identities for Organizations
+"""
+type OrganizationIdentityProvider implements Node {
+  """
+  The digest algorithm used to sign SAML requests for the Identity Provider.
+  """
+  digestMethod: URI
+
+  """External Identities provisioned by this Identity Provider"""
+  externalIdentities(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ExternalIdentityConnection!
+  id: ID!
+
+  """
+  The x509 certificate used by the Identity Provder to sign assertions and responses.
+  """
+  idpCertificate: X509Certificate
+
+  """The Issuer Entity ID for the SAML Identity Provider"""
+  issuer: String
+
+  """Organization this Identity Provider belongs to"""
+  organization: Organization
+
+  """
+  The signature algorithm used to sign SAML requests for the Identity Provider.
+  """
+  signatureMethod: URI
+
+  """The URL endpoint for the Identity Provider's SAML SSO."""
+  ssoUrl: URI
+}
+
+"""An Invitation for a user to an organization."""
+type OrganizationInvitation implements Node {
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The email address of the user invited to the organization."""
+  email: String
+  id: ID!
+
+  """The type of invitation that was sent (e.g. email, user)."""
+  invitationType: OrganizationInvitationType!
+
+  """The user who was invited to the organization."""
+  invitee: User
+
+  """The user who created the invitation."""
+  inviter: User!
+
+  """The organization the invite is for"""
+  organization: Organization!
+
+  """The user's pending role in the organization (e.g. member, owner)."""
+  role: OrganizationInvitationRole!
+}
+
+"""The connection type for OrganizationInvitation."""
+type OrganizationInvitationConnection {
+  """A list of edges."""
+  edges: [OrganizationInvitationEdge]
+
+  """A list of nodes."""
+  nodes: [OrganizationInvitation]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type OrganizationInvitationEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: OrganizationInvitation
+}
+
+"""The possible organization invitation roles."""
+enum OrganizationInvitationRole {
+  """The user is invited to be a direct member of the organization."""
+  DIRECT_MEMBER
+
+  """The user is invited to be an admin of the organization."""
+  ADMIN
+
+  """The user is invited to be a billing manager of the organization."""
+  BILLING_MANAGER
+
+  """The user's previous role will be reinstated."""
+  REINSTATE
+}
+
+"""The possible organization invitation types."""
+enum OrganizationInvitationType {
+  """The invitation was to an existing user."""
+  USER
+
+  """The invitation was to an email address."""
+  EMAIL
+}
+
+"""The connection type for User."""
+type OrganizationMemberConnection {
+  """A list of edges."""
+  edges: [OrganizationMemberEdge]
+
+  """A list of nodes."""
+  nodes: [User]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a user within an organization."""
+type OrganizationMemberEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """
+  Whether the organization member has two factor enabled or not. Returns null if information is not available to viewer.
+  """
+  hasTwoFactorEnabled: Boolean
+
+  """The item at the end of the edge."""
+  node: User
+
+  """The role this user has in the organization."""
+  role: OrganizationMemberRole
+}
+
+"""The possible roles within an organization for its members."""
+enum OrganizationMemberRole {
+  """The user is a member of the organization."""
+  MEMBER
+
+  """The user is an administrator of the organization."""
+  ADMIN
+}
+
+"""Information about pagination in a connection."""
+type PageInfo {
+  """When paginating forwards, the cursor to continue."""
+  endCursor: String
+
+  """When paginating forwards, are there more items?"""
+  hasNextPage: Boolean!
+
+  """When paginating backwards, are there more items?"""
+  hasPreviousPage: Boolean!
+
+  """When paginating backwards, the cursor to continue."""
+  startCursor: String
+}
+
+"""Types that can grant permissions on a repository to a user"""
+union PermissionGranter = Organization | Repository | Team
+
+"""A level of permission and source for a user's access to a repository."""
+type PermissionSource {
+  """The organization the repository belongs to."""
+  organization: Organization!
+
+  """The level of access this source has granted to the user."""
+  permission: DefaultRepositoryPermissionField!
+
+  """The source of this permission."""
+  source: PermissionGranter!
+}
+
+"""Autogenerated input type of PinIssue"""
+input PinIssueInput {
+  """The ID of the issue to be pinned"""
+  issueId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Types that can be pinned to a profile page."""
+union PinnableItem = Gist | Repository
+
+"""The connection type for PinnableItem."""
+type PinnableItemConnection {
+  """A list of edges."""
+  edges: [PinnableItemEdge]
+
+  """A list of nodes."""
+  nodes: [PinnableItem]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type PinnableItemEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PinnableItem
+}
+
+"""Represents items that can be pinned to a profile page or dashboard."""
+enum PinnableItemType {
+  """A repository."""
+  REPOSITORY
+
+  """A gist."""
+  GIST
+
+  """An issue."""
+  ISSUE
+}
+
+"""Represents a 'pinned' event on a given issue or pull request."""
+type PinnedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Identifies the issue associated with the event."""
+  issue: Issue!
+}
+
+"""
+A curatable list of repositories relating to a repository owner, which defaults
+to showing the most popular repositories they own.
+"""
+type ProfileItemShowcase {
+  """Whether or not the owner has pinned any repositories or gists."""
+  hasPinnedItems: Boolean!
+
+  """
+  The repositories and gists in the showcase. If the profile owner has any
+  pinned items, those will be returned. Otherwise, the profile owner's popular
+  repositories will be returned.
+  """
+  items(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PinnableItemConnection!
+}
+
+"""Represents any entity on GitHub that has a profile page."""
+interface ProfileOwner {
+  """
+  Determine if this repository owner has any items that can be pinned to their profile.
+  """
+  anyPinnableItems(
+    """Filter to only a particular kind of pinnable item."""
+    type: PinnableItemType
+  ): Boolean!
+
+  """The public profile email."""
+  email: String
+  id: ID!
+
+  """
+  Showcases a selection of repositories and gists that the profile owner has
+  either curated or that have been selected automatically based on popularity.
+  """
+  itemShowcase: ProfileItemShowcase!
+
+  """The public profile location."""
+  location: String
+
+  """The username used to login."""
+  login: String!
+
+  """The public profile name."""
+  name: String
+
+  """
+  A list of repositories and gists this profile owner can pin to their profile.
+  """
+  pinnableItems(
+    """Filter the types of pinnable items that are returned."""
+    types: [PinnableItemType!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PinnableItemConnection!
+
+  """
+  A list of repositories and gists this profile owner has pinned to their profile
+  """
+  pinnedItems(
+    """Filter the types of pinned items that are returned."""
+    types: [PinnableItemType!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PinnableItemConnection!
+
+  """
+  Returns how many more items this profile owner can pin to their profile.
+  """
+  pinnedItemsRemaining: Int!
+
+  """Can the viewer pin repositories and gists to the profile?"""
+  viewerCanChangePinnedItems: Boolean!
+
+  """The public profile website URL."""
+  websiteUrl: URI
+}
+
+"""
+Projects manage issues, pull requests and notes within a project owner.
+"""
+type Project implements Node & Closable & Updatable {
+  """The project's description body."""
+  body: String
+
+  """The projects description body rendered to HTML."""
+  bodyHTML: HTML!
+
+  """
+  `true` if the object is closed (definition of closed may depend on type)
+  """
+  closed: Boolean!
+
+  """Identifies the date and time when the object was closed."""
+  closedAt: DateTime
+
+  """List of columns in the project"""
+  columns(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ProjectColumnConnection!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The actor who originally created the project."""
+  creator: Actor
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+
+  """The project's name."""
+  name: String!
+
+  """The project's number."""
+  number: Int!
+
+  """
+  The project's owner. Currently limited to repositories, organizations, and users.
+  """
+  owner: ProjectOwner!
+
+  """List of pending cards in this project"""
+  pendingCards(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """A list of archived states to filter the cards by"""
+    archivedStates: [ProjectCardArchivedState]
+  ): ProjectCardConnection!
+
+  """The HTTP path for this project"""
+  resourcePath: URI!
+
+  """Whether the project is open or closed."""
+  state: ProjectState!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this project"""
+  url: URI!
+
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+}
+
+"""A card in a project."""
+type ProjectCard implements Node {
+  """
+  The project column this card is associated under. A card may only belong to one
+  project column at a time. The column field will be null if the card is created
+  in a pending state and has yet to be associated with a column. Once cards are
+  associated with a column, they will not become pending in the future.
+
+  """
+  column: ProjectColumn
+
+  """The card content item"""
+  content: ProjectCardItem
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The actor who created this card"""
+  creator: Actor
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+
+  """Whether the card is archived"""
+  isArchived: Boolean!
+
+  """The card note"""
+  note: String
+
+  """The project that contains this card."""
+  project: Project!
+
+  """The HTTP path for this card"""
+  resourcePath: URI!
+
+  """The state of ProjectCard"""
+  state: ProjectCardState
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this card"""
+  url: URI!
+}
+
+"""The possible archived states of a project card."""
+enum ProjectCardArchivedState {
+  """A project card that is archived"""
+  ARCHIVED
+
+  """A project card that is not archived"""
+  NOT_ARCHIVED
+}
+
+"""The connection type for ProjectCard."""
+type ProjectCardConnection {
+  """A list of edges."""
+  edges: [ProjectCardEdge]
+
+  """A list of nodes."""
+  nodes: [ProjectCard]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type ProjectCardEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: ProjectCard
+}
+
+"""An issue or PR and its owning repository to be used in a project card."""
+input ProjectCardImport {
+  """Repository name with owner (owner/repository)."""
+  repository: String!
+
+  """The issue or pull request number."""
+  number: Int!
+}
+
+"""Types that can be inside Project Cards."""
+union ProjectCardItem = Issue | PullRequest
+
+"""Various content states of a ProjectCard"""
+enum ProjectCardState {
+  """The card has content only."""
+  CONTENT_ONLY
+
+  """The card has a note only."""
+  NOTE_ONLY
+
+  """The card is redacted."""
+  REDACTED
+}
+
+"""A column inside a project."""
+type ProjectColumn implements Node {
+  """List of cards in the column"""
+  cards(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """A list of archived states to filter the cards by"""
+    archivedStates: [ProjectCardArchivedState]
+  ): ProjectCardConnection!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+
+  """The project column's name."""
+  name: String!
+
+  """The project that contains this column."""
+  project: Project!
+
+  """The semantic purpose of the column"""
+  purpose: ProjectColumnPurpose
+
+  """The HTTP path for this project column"""
+  resourcePath: URI!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this project column"""
+  url: URI!
+}
+
+"""The connection type for ProjectColumn."""
+type ProjectColumnConnection {
+  """A list of edges."""
+  edges: [ProjectColumnEdge]
+
+  """A list of nodes."""
+  nodes: [ProjectColumn]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type ProjectColumnEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: ProjectColumn
+}
+
+"""A project column and a list of its issues and PRs."""
+input ProjectColumnImport {
+  """The name of the column."""
+  columnName: String!
+
+  """The position of the column, starting from 0."""
+  position: Int!
+
+  """A list of issues and pull requests in the column."""
+  issues: [ProjectCardImport!]
+}
+
+"""The semantic purpose of the column - todo, in progress, or done."""
+enum ProjectColumnPurpose {
+  """The column contains cards still to be worked on"""
+  TODO
+
+  """The column contains cards which are currently being worked on"""
+  IN_PROGRESS
+
+  """The column contains cards which are complete"""
+  DONE
+}
+
+"""A list of projects associated with the owner."""
+type ProjectConnection {
+  """A list of edges."""
+  edges: [ProjectEdge]
+
+  """A list of nodes."""
+  nodes: [Project]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type ProjectEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Project
+}
+
+"""Ways in which lists of projects can be ordered upon return."""
+input ProjectOrder {
+  """The field in which to order projects by."""
+  field: ProjectOrderField!
+
+  """The direction in which to order projects by the specified field."""
+  direction: OrderDirection!
+}
+
+"""Properties by which project connections can be ordered."""
+enum ProjectOrderField {
+  """Order projects by creation time"""
+  CREATED_AT
+
+  """Order projects by update time"""
+  UPDATED_AT
+
+  """Order projects by name"""
+  NAME
+}
+
+"""Represents an owner of a Project."""
+interface ProjectOwner {
+  id: ID!
+
+  """Find project by number."""
+  project(
+    """The project number to find."""
+    number: Int!
+  ): Project
+
+  """A list of projects under the owner."""
+  projects(
+    """Ordering options for projects returned from the connection"""
+    orderBy: ProjectOrder
+
+    """Query to search projects by, currently only searching by name."""
+    search: String
+
+    """A list of states to filter the projects by."""
+    states: [ProjectState!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ProjectConnection!
+
+  """The HTTP path listing owners projects"""
+  projectsResourcePath: URI!
+
+  """The HTTP URL listing owners projects"""
+  projectsUrl: URI!
+
+  """Can the current viewer create new projects on this owner."""
+  viewerCanCreateProjects: Boolean!
+}
+
+"""State of the project; either 'open' or 'closed'"""
+enum ProjectState {
+  """The project is open."""
+  OPEN
+
+  """The project is closed."""
+  CLOSED
+}
+
+"""A user's public key."""
+type PublicKey implements Node {
+  """The last time this authorization was used to perform an action"""
+  accessedAt: DateTime
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The fingerprint for this PublicKey"""
+  fingerprint: String
+  id: ID!
+
+  """Whether this PublicKey is read-only or not"""
+  isReadOnly: Boolean!
+
+  """The public key string"""
+  key: String!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+}
+
+"""The connection type for PublicKey."""
+type PublicKeyConnection {
+  """A list of edges."""
+  edges: [PublicKeyEdge]
+
+  """A list of nodes."""
+  nodes: [PublicKey]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type PublicKeyEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PublicKey
+}
+
+"""A repository pull request."""
+type PullRequest implements Node & Assignable & Closable & Comment & Updatable & UpdatableComment & Labelable & Lockable & Reactable & RepositoryNode & Subscribable & UniformResourceLocatable {
+  """Reason that the conversation was locked."""
+  activeLockReason: LockReason
+
+  """The number of additions in this pull request."""
+  additions: Int!
+
+  """A list of Users assigned to this object."""
+  assignees(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+
+  """The actor who authored the comment."""
+  author: Actor
+
+  """Author's association with the subject of the comment."""
+  authorAssociation: CommentAuthorAssociation!
+
+  """Identifies the base Ref associated with the pull request."""
+  baseRef: Ref
+
+  """
+  Identifies the name of the base Ref associated with the pull request, even if the ref has been deleted.
+  """
+  baseRefName: String!
+
+  """
+  Identifies the oid of the base ref associated with the pull request, even if the ref has been deleted.
+  """
+  baseRefOid: GitObjectID!
+
+  """The repository associated with this pull request's base Ref."""
+  baseRepository: Repository
+
+  """The body as Markdown."""
+  body: String!
+
+  """The body rendered to HTML."""
+  bodyHTML: HTML!
+
+  """The body rendered to text."""
+  bodyText: String!
+
+  """The number of changed files in this pull request."""
+  changedFiles: Int!
+
+  """`true` if the pull request is closed"""
+  closed: Boolean!
+
+  """Identifies the date and time when the object was closed."""
+  closedAt: DateTime
+
+  """A list of comments associated with the pull request."""
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueCommentConnection!
+
+  """
+  A list of commits present in this pull request's head branch not present in the base branch.
+  """
+  commits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestCommitConnection!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Check if this comment was created via an email reply."""
+  createdViaEmail: Boolean!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The number of deletions in this pull request."""
+  deletions: Int!
+
+  """The actor who edited this pull request's body."""
+  editor: Actor
+
+  """Lists the files changed within this pull request."""
+  files(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestChangedFileConnection
+
+  """Identifies the head Ref associated with the pull request."""
+  headRef: Ref
+
+  """
+  Identifies the name of the head Ref associated with the pull request, even if the ref has been deleted.
+  """
+  headRefName: String!
+
+  """
+  Identifies the oid of the head ref associated with the pull request, even if the ref has been deleted.
+  """
+  headRefOid: GitObjectID!
+
+  """The repository associated with this pull request's head Ref."""
+  headRepository: Repository
+
+  """
+  The owner of the repository associated with this pull request's head Ref.
+  """
+  headRepositoryOwner: RepositoryOwner
+  id: ID!
+
+  """
+  Check if this comment was edited and includes an edit with the creation data
+  """
+  includesCreatedEdit: Boolean!
+
+  """The head and base repositories are different."""
+  isCrossRepository: Boolean!
+
+  """A list of labels associated with the object."""
+  labels(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): LabelConnection
+
+  """The moment the editor made the last edit"""
+  lastEditedAt: DateTime
+
+  """`true` if the pull request is locked"""
+  locked: Boolean!
+
+  """Indicates whether maintainers can modify the pull request."""
+  maintainerCanModify: Boolean!
+
+  """The commit that was created when this pull request was merged."""
+  mergeCommit: Commit
+
+  """
+  Whether or not the pull request can be merged based on the existence of merge conflicts.
+  """
+  mergeable: MergeableState!
+
+  """Whether or not the pull request was merged."""
+  merged: Boolean!
+
+  """The date and time that the pull request was merged."""
+  mergedAt: DateTime
+
+  """The actor who merged the pull request."""
+  mergedBy: Actor
+
+  """Identifies the milestone associated with the pull request."""
+  milestone: Milestone
+
+  """Identifies the pull request number."""
+  number: Int!
+
+  """
+  A list of Users that are participating in the Pull Request conversation.
+  """
+  participants(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+
+  """The permalink to the pull request."""
+  permalink: URI!
+
+  """
+  The commit that GitHub automatically generated to test if this pull request
+  could be merged. This field will not return a value if the pull request is
+  merged, or if the test merge commit is still being generated. See the
+  `mergeable` field for more details on the mergeability of the pull request.
+  """
+  potentialMergeCommit: Commit
+
+  """List of project cards associated with this pull request."""
+  projectCards(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """A list of archived states to filter the cards by"""
+    archivedStates: [ProjectCardArchivedState]
+  ): ProjectCardConnection!
+
+  """Identifies when the comment was published at."""
+  publishedAt: DateTime
+
+  """A list of reactions grouped by content left on the subject."""
+  reactionGroups: [ReactionGroup!]
+
+  """A list of Reactions left on the Issue."""
+  reactions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Allows filtering Reactions by emoji."""
+    content: ReactionContent
+
+    """Allows specifying the order in which reactions are returned."""
+    orderBy: ReactionOrder
+  ): ReactionConnection!
+
+  """The repository associated with this node."""
+  repository: Repository!
+
+  """The HTTP path for this pull request."""
+  resourcePath: URI!
+
+  """The HTTP path for reverting this pull request."""
+  revertResourcePath: URI!
+
+  """The HTTP URL for reverting this pull request."""
+  revertUrl: URI!
+
+  """A list of review requests associated with the pull request."""
+  reviewRequests(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ReviewRequestConnection
+
+  """The list of all review threads for this pull request."""
+  reviewThreads(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestReviewThreadConnection!
+
+  """A list of reviews associated with the pull request."""
+  reviews(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """A list of states to filter the reviews."""
+    states: [PullRequestReviewState!]
+
+    """Filter by author of the review."""
+    author: String
+  ): PullRequestReviewConnection
+
+  """Identifies the state of the pull request."""
+  state: PullRequestState!
+
+  """
+  A list of reviewer suggestions based on commit history and past review comments.
+  """
+  suggestedReviewers: [SuggestedReviewer]!
+
+  """
+  A list of events, comments, commits, etc. associated with the pull request.
+  """
+  timeline(
+    """Allows filtering timeline events by a `since` timestamp."""
+    since: DateTime
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestTimelineConnection!
+
+  """
+  A list of events, comments, commits, etc. associated with the pull request.
+  """
+  timelineItems(
+    """Filter timeline items by a `since` timestamp."""
+    since: DateTime
+
+    """Skips the first _n_ elements in the list."""
+    skip: Int
+
+    """Filter timeline items by type."""
+    itemTypes: [PullRequestTimelineItemsItemType!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestTimelineItemsConnection!
+
+  """Identifies the pull request title."""
+  title: String!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this pull request."""
+  url: URI!
+
+  """A list of edits to this content."""
+  userContentEdits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserContentEditConnection
+
+  """Whether or not the viewer can apply suggestion."""
+  viewerCanApplySuggestion: Boolean!
+
+  """Can user react to this subject"""
+  viewerCanReact: Boolean!
+
+  """
+  Check if the viewer is able to change their subscription status for the repository.
+  """
+  viewerCanSubscribe: Boolean!
+
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+
+  """Reasons why the current viewer can not update this comment."""
+  viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+  """Did the viewer author this comment."""
+  viewerDidAuthor: Boolean!
+
+  """
+  Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+  """
+  viewerSubscription: SubscriptionState
+}
+
+"""A file changed in a pull request."""
+type PullRequestChangedFile {
+  """The number of additions to the file."""
+  additions: Int!
+
+  """The number of deletions to the file."""
+  deletions: Int!
+
+  """The path of the file."""
+  path: String!
+}
+
+"""The connection type for PullRequestChangedFile."""
+type PullRequestChangedFileConnection {
+  """A list of edges."""
+  edges: [PullRequestChangedFileEdge]
+
+  """A list of nodes."""
+  nodes: [PullRequestChangedFile]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type PullRequestChangedFileEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PullRequestChangedFile
+}
+
+"""Represents a Git commit part of a pull request."""
+type PullRequestCommit implements Node & UniformResourceLocatable {
+  """The Git commit object"""
+  commit: Commit!
+  id: ID!
+
+  """The pull request this commit belongs to"""
+  pullRequest: PullRequest!
+
+  """The HTTP path for this pull request commit"""
+  resourcePath: URI!
+
+  """The HTTP URL for this pull request commit"""
+  url: URI!
+}
+
+"""Represents a commit comment thread part of a pull request."""
+type PullRequestCommitCommentThread implements Node & RepositoryNode {
+  """The comments that exist in this thread."""
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): CommitCommentConnection!
+
+  """The commit the comments were made on."""
+  commit: Commit!
+  id: ID!
+
+  """The file the comments were made on."""
+  path: String
+
+  """The position in the diff for the commit that the comment was made on."""
+  position: Int
+
+  """The pull request this commit comment thread belongs to"""
+  pullRequest: PullRequest!
+
+  """The repository associated with this node."""
+  repository: Repository!
+}
+
+"""The connection type for PullRequestCommit."""
+type PullRequestCommitConnection {
+  """A list of edges."""
+  edges: [PullRequestCommitEdge]
+
+  """A list of nodes."""
+  nodes: [PullRequestCommit]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type PullRequestCommitEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PullRequestCommit
+}
+
+"""The connection type for PullRequest."""
+type PullRequestConnection {
+  """A list of edges."""
+  edges: [PullRequestEdge]
+
+  """A list of nodes."""
+  nodes: [PullRequest]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""This aggregates pull requests opened by a user within one repository."""
+type PullRequestContributionsByRepository {
+  """The pull request contributions."""
+  contributions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Ordering options for contributions returned from the connection."""
+    orderBy: ContributionOrder
+  ): CreatedPullRequestContributionConnection!
+
+  """The repository in which the pull requests were opened."""
+  repository: Repository!
+}
+
+"""An edge in a connection."""
+type PullRequestEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PullRequest
+}
+
+"""Ways in which lists of issues can be ordered upon return."""
+input PullRequestOrder {
+  """The field in which to order pull requests by."""
+  field: PullRequestOrderField!
+
+  """The direction in which to order pull requests by the specified field."""
+  direction: OrderDirection!
+}
+
+"""Properties by which pull_requests connections can be ordered."""
+enum PullRequestOrderField {
+  """Order pull_requests by creation time"""
+  CREATED_AT
+
+  """Order pull_requests by update time"""
+  UPDATED_AT
+}
+
+"""The possible PubSub channels for a pull request."""
+enum PullRequestPubSubTopic {
+  """The channel ID for observing pull request updates."""
+  UPDATED
+
+  """The channel ID for marking an pull request as read."""
+  MARKASREAD
+
+  """The channel ID for observing head ref updates."""
+  HEAD_REF
+
+  """The channel ID for updating items on the pull request timeline."""
+  TIMELINE
+
+  """The channel ID for observing pull request state updates."""
+  STATE
+}
+
+"""A review object for a given pull request."""
+type PullRequestReview implements Node & Comment & Deletable & Updatable & UpdatableComment & Reactable & RepositoryNode {
+  """The actor who authored the comment."""
+  author: Actor
+
+  """Author's association with the subject of the comment."""
+  authorAssociation: CommentAuthorAssociation!
+
+  """Identifies the pull request review body."""
+  body: String!
+
+  """The body of this review rendered to HTML."""
+  bodyHTML: HTML!
+
+  """The body of this review rendered as plain text."""
+  bodyText: String!
+
+  """A list of review comments for the current pull request review."""
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestReviewCommentConnection!
+
+  """Identifies the commit associated with this pull request review."""
+  commit: Commit
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Check if this comment was created via an email reply."""
+  createdViaEmail: Boolean!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The actor who edited the comment."""
+  editor: Actor
+  id: ID!
+
+  """
+  Check if this comment was edited and includes an edit with the creation data
+  """
+  includesCreatedEdit: Boolean!
+
+  """The moment the editor made the last edit"""
+  lastEditedAt: DateTime
+
+  """A list of teams that this review was made on behalf of."""
+  onBehalfOf(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): TeamConnection!
+
+  """Identifies when the comment was published at."""
+  publishedAt: DateTime
+
+  """Identifies the pull request associated with this pull request review."""
+  pullRequest: PullRequest!
+
+  """A list of reactions grouped by content left on the subject."""
+  reactionGroups: [ReactionGroup!]
+
+  """A list of Reactions left on the Issue."""
+  reactions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Allows filtering Reactions by emoji."""
+    content: ReactionContent
+
+    """Allows specifying the order in which reactions are returned."""
+    orderBy: ReactionOrder
+  ): ReactionConnection!
+
+  """The repository associated with this node."""
+  repository: Repository!
+
+  """The HTTP path permalink for this PullRequestReview."""
+  resourcePath: URI!
+
+  """Identifies the current state of the pull request review."""
+  state: PullRequestReviewState!
+
+  """Identifies when the Pull Request Review was submitted"""
+  submittedAt: DateTime
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL permalink for this PullRequestReview."""
+  url: URI!
+
+  """A list of edits to this content."""
+  userContentEdits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserContentEditConnection
+
+  """Check if the current viewer can delete this object."""
+  viewerCanDelete: Boolean!
+
+  """Can user react to this subject"""
+  viewerCanReact: Boolean!
+
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+
+  """Reasons why the current viewer can not update this comment."""
+  viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+  """Did the viewer author this comment."""
+  viewerDidAuthor: Boolean!
+}
+
+"""A review comment associated with a given repository pull request."""
+type PullRequestReviewComment implements Node & Comment & Deletable & Updatable & UpdatableComment & Reactable & RepositoryNode {
+  """The actor who authored the comment."""
+  author: Actor
+
+  """Author's association with the subject of the comment."""
+  authorAssociation: CommentAuthorAssociation!
+
+  """The comment body of this review comment."""
+  body: String!
+
+  """The comment body of this review comment rendered to HTML."""
+  bodyHTML: HTML!
+
+  """The comment body of this review comment rendered as plain text."""
+  bodyText: String!
+
+  """Identifies the commit associated with the comment."""
+  commit: Commit!
+
+  """Identifies when the comment was created."""
+  createdAt: DateTime!
+
+  """Check if this comment was created via an email reply."""
+  createdViaEmail: Boolean!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The diff hunk to which the comment applies."""
+  diffHunk: String!
+
+  """Identifies when the comment was created in a draft state."""
+  draftedAt: DateTime!
+
+  """The actor who edited the comment."""
+  editor: Actor
+  id: ID!
+
+  """
+  Check if this comment was edited and includes an edit with the creation data
+  """
+  includesCreatedEdit: Boolean!
+
+  """Returns whether or not a comment has been minimized."""
+  isMinimized: Boolean!
+
+  """The moment the editor made the last edit"""
+  lastEditedAt: DateTime
+
+  """Returns why the comment was minimized."""
+  minimizedReason: String
+
+  """Identifies the original commit associated with the comment."""
+  originalCommit: Commit
+
+  """The original line index in the diff to which the comment applies."""
+  originalPosition: Int!
+
+  """Identifies when the comment body is outdated"""
+  outdated: Boolean!
+
+  """The path to which the comment applies."""
+  path: String!
+
+  """The line index in the diff to which the comment applies."""
+  position: Int
+
+  """Identifies when the comment was published at."""
+  publishedAt: DateTime
+
+  """The pull request associated with this review comment."""
+  pullRequest: PullRequest!
+
+  """The pull request review associated with this review comment."""
+  pullRequestReview: PullRequestReview
+
+  """A list of reactions grouped by content left on the subject."""
+  reactionGroups: [ReactionGroup!]
+
+  """A list of Reactions left on the Issue."""
+  reactions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Allows filtering Reactions by emoji."""
+    content: ReactionContent
+
+    """Allows specifying the order in which reactions are returned."""
+    orderBy: ReactionOrder
+  ): ReactionConnection!
+
+  """The comment this is a reply to."""
+  replyTo: PullRequestReviewComment
+
+  """The repository associated with this node."""
+  repository: Repository!
+
+  """The HTTP path permalink for this review comment."""
+  resourcePath: URI!
+
+  """Identifies the state of the comment."""
+  state: PullRequestReviewCommentState!
+
+  """Identifies when the comment was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL permalink for this review comment."""
+  url: URI!
+
+  """A list of edits to this content."""
+  userContentEdits(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserContentEditConnection
+
+  """Check if the current viewer can delete this object."""
+  viewerCanDelete: Boolean!
+
+  """Check if the current viewer can minimize this object."""
+  viewerCanMinimize: Boolean!
+
+  """Can user react to this subject"""
+  viewerCanReact: Boolean!
+
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+
+  """Reasons why the current viewer can not update this comment."""
+  viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+
+  """Did the viewer author this comment."""
+  viewerDidAuthor: Boolean!
+}
+
+"""The connection type for PullRequestReviewComment."""
+type PullRequestReviewCommentConnection {
+  """A list of edges."""
+  edges: [PullRequestReviewCommentEdge]
+
+  """A list of nodes."""
+  nodes: [PullRequestReviewComment]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type PullRequestReviewCommentEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PullRequestReviewComment
+}
+
+"""The possible states of a pull request review comment."""
+enum PullRequestReviewCommentState {
+  """A comment that is part of a pending review"""
+  PENDING
+
+  """A comment that is part of a submitted review"""
+  SUBMITTED
+}
+
+"""The connection type for PullRequestReview."""
+type PullRequestReviewConnection {
+  """A list of edges."""
+  edges: [PullRequestReviewEdge]
+
+  """A list of nodes."""
+  nodes: [PullRequestReview]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""
+This aggregates pull request reviews made by a user within one repository.
+"""
+type PullRequestReviewContributionsByRepository {
+  """The pull request review contributions."""
+  contributions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Ordering options for contributions returned from the connection."""
+    orderBy: ContributionOrder
+  ): CreatedPullRequestReviewContributionConnection!
+
+  """The repository in which the pull request reviews were made."""
+  repository: Repository!
+}
+
+"""An edge in a connection."""
+type PullRequestReviewEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PullRequestReview
+}
+
+"""The possible events to perform on a pull request review."""
+enum PullRequestReviewEvent {
+  """Submit general feedback without explicit approval."""
+  COMMENT
+
+  """Submit feedback and approve merging these changes."""
+  APPROVE
+
+  """Submit feedback that must be addressed before merging."""
+  REQUEST_CHANGES
+
+  """Dismiss review so it now longer effects merging."""
+  DISMISS
+}
+
+"""The possible states of a pull request review."""
+enum PullRequestReviewState {
+  """A review that has not yet been submitted."""
+  PENDING
+
+  """An informational review."""
+  COMMENTED
+
+  """A review allowing the pull request to merge."""
+  APPROVED
+
+  """A review blocking the pull request from merging."""
+  CHANGES_REQUESTED
+
+  """A review that has been dismissed."""
+  DISMISSED
+}
+
+"""A threaded list of comments for a given pull request."""
+type PullRequestReviewThread implements Node {
+  """A list of pull request comments associated with the thread."""
+  comments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestReviewCommentConnection!
+  id: ID!
+
+  """Whether this thread has been resolved"""
+  isResolved: Boolean!
+
+  """Identifies the pull request associated with this thread."""
+  pullRequest: PullRequest!
+
+  """Identifies the repository associated with this thread."""
+  repository: Repository!
+
+  """The user who resolved this thread"""
+  resolvedBy: User
+
+  """Whether or not the viewer can resolve this thread"""
+  viewerCanResolve: Boolean!
+
+  """Whether or not the viewer can unresolve this thread"""
+  viewerCanUnresolve: Boolean!
+}
+
+"""Review comment threads for a pull request review."""
+type PullRequestReviewThreadConnection {
+  """A list of edges."""
+  edges: [PullRequestReviewThreadEdge]
+
+  """A list of nodes."""
+  nodes: [PullRequestReviewThread]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type PullRequestReviewThreadEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PullRequestReviewThread
+}
+
+"""
+Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.
+"""
+type PullRequestRevisionMarker {
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The last commit the viewer has seen."""
+  lastSeenCommit: Commit!
+
+  """The pull request to which the marker belongs."""
+  pullRequest: PullRequest!
+}
+
+"""The possible states of a pull request."""
+enum PullRequestState {
+  """A pull request that is still open."""
+  OPEN
+
+  """A pull request that has been closed without being merged."""
+  CLOSED
+
+  """A pull request that has been closed by being merged."""
+  MERGED
+}
+
+"""The connection type for PullRequestTimelineItem."""
+type PullRequestTimelineConnection {
+  """A list of edges."""
+  edges: [PullRequestTimelineItemEdge]
+
+  """A list of nodes."""
+  nodes: [PullRequestTimelineItem]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An item in an pull request timeline"""
+union PullRequestTimelineItem = Commit | CommitCommentThread | PullRequestReview | PullRequestReviewThread | PullRequestReviewComment | IssueComment | ClosedEvent | ReopenedEvent | SubscribedEvent | UnsubscribedEvent | MergedEvent | ReferencedEvent | CrossReferencedEvent | AssignedEvent | UnassignedEvent | LabeledEvent | UnlabeledEvent | MilestonedEvent | DemilestonedEvent | RenamedTitleEvent | LockedEvent | UnlockedEvent | DeployedEvent | DeploymentEnvironmentChangedEvent | HeadRefDeletedEvent | HeadRefRestoredEvent | HeadRefForcePushedEvent | BaseRefForcePushedEvent | ReviewRequestedEvent | ReviewRequestRemovedEvent | ReviewDismissedEvent | UserBlockedEvent
+
+"""An edge in a connection."""
+type PullRequestTimelineItemEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PullRequestTimelineItem
+}
+
+"""An item in a pull request timeline"""
+union PullRequestTimelineItems = PullRequestCommit | PullRequestCommitCommentThread | PullRequestReview | PullRequestReviewThread | PullRequestRevisionMarker | BaseRefChangedEvent | BaseRefForcePushedEvent | DeployedEvent | DeploymentEnvironmentChangedEvent | HeadRefDeletedEvent | HeadRefForcePushedEvent | HeadRefRestoredEvent | MergedEvent | ReviewDismissedEvent | ReviewRequestedEvent | ReviewRequestRemovedEvent | IssueComment | CrossReferencedEvent | AddedToProjectEvent | AssignedEvent | ClosedEvent | CommentDeletedEvent | ConvertedNoteToIssueEvent | DemilestonedEvent | LabeledEvent | LockedEvent | MentionedEvent | MilestonedEvent | MovedColumnsInProjectEvent | PinnedEvent | ReferencedEvent | RemovedFromProjectEvent | RenamedTitleEvent | ReopenedEvent | SubscribedEvent | TransferredEvent | UnassignedEvent | UnlabeledEvent | UnlockedEvent | UserBlockedEvent | UnpinnedEvent | UnsubscribedEvent
+
+"""The connection type for PullRequestTimelineItems."""
+type PullRequestTimelineItemsConnection {
+  """A list of edges."""
+  edges: [PullRequestTimelineItemsEdge]
+
+  """
+  Identifies the count of items after applying `before` and `after` filters.
+  """
+  filteredCount: Int!
+
+  """A list of nodes."""
+  nodes: [PullRequestTimelineItems]
+
+  """
+  Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.
+  """
+  pageCount: Int!
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+
+  """Identifies the date and time when the timeline was last updated."""
+  updatedAt: DateTime!
+}
+
+"""An edge in a connection."""
+type PullRequestTimelineItemsEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PullRequestTimelineItems
+}
+
+"""The possible item types found in a timeline."""
+enum PullRequestTimelineItemsItemType {
+  """Represents a Git commit part of a pull request."""
+  PULL_REQUEST_COMMIT
+
+  """Represents a commit comment thread part of a pull request."""
+  PULL_REQUEST_COMMIT_COMMENT_THREAD
+
+  """A review object for a given pull request."""
+  PULL_REQUEST_REVIEW
+
+  """A threaded list of comments for a given pull request."""
+  PULL_REQUEST_REVIEW_THREAD
+
+  """
+  Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.
+  """
+  PULL_REQUEST_REVISION_MARKER
+
+  """
+  Represents a 'base_ref_changed' event on a given issue or pull request.
+  """
+  BASE_REF_CHANGED_EVENT
+
+  """Represents a 'base_ref_force_pushed' event on a given pull request."""
+  BASE_REF_FORCE_PUSHED_EVENT
+
+  """Represents a 'deployed' event on a given pull request."""
+  DEPLOYED_EVENT
+
+  """
+  Represents a 'deployment_environment_changed' event on a given pull request.
+  """
+  DEPLOYMENT_ENVIRONMENT_CHANGED_EVENT
+
+  """Represents a 'head_ref_deleted' event on a given pull request."""
+  HEAD_REF_DELETED_EVENT
+
+  """Represents a 'head_ref_force_pushed' event on a given pull request."""
+  HEAD_REF_FORCE_PUSHED_EVENT
+
+  """Represents a 'head_ref_restored' event on a given pull request."""
+  HEAD_REF_RESTORED_EVENT
+
+  """Represents a 'merged' event on a given pull request."""
+  MERGED_EVENT
+
+  """
+  Represents a 'review_dismissed' event on a given issue or pull request.
+  """
+  REVIEW_DISMISSED_EVENT
+
+  """Represents an 'review_requested' event on a given pull request."""
+  REVIEW_REQUESTED_EVENT
+
+  """Represents an 'review_request_removed' event on a given pull request."""
+  REVIEW_REQUEST_REMOVED_EVENT
+
+  """Represents a comment on an Issue."""
+  ISSUE_COMMENT
+
+  """Represents a mention made by one issue or pull request to another."""
+  CROSS_REFERENCED_EVENT
+
+  """
+  Represents a 'added_to_project' event on a given issue or pull request.
+  """
+  ADDED_TO_PROJECT_EVENT
+
+  """Represents an 'assigned' event on any assignable object."""
+  ASSIGNED_EVENT
+
+  """Represents a 'closed' event on any `Closable`."""
+  CLOSED_EVENT
+
+  """Represents a 'comment_deleted' event on a given issue or pull request."""
+  COMMENT_DELETED_EVENT
+
+  """
+  Represents a 'converted_note_to_issue' event on a given issue or pull request.
+  """
+  CONVERTED_NOTE_TO_ISSUE_EVENT
+
+  """Represents a 'demilestoned' event on a given issue or pull request."""
+  DEMILESTONED_EVENT
+
+  """Represents a 'labeled' event on a given issue or pull request."""
+  LABELED_EVENT
+
+  """Represents a 'locked' event on a given issue or pull request."""
+  LOCKED_EVENT
+
+  """Represents a 'mentioned' event on a given issue or pull request."""
+  MENTIONED_EVENT
+
+  """Represents a 'milestoned' event on a given issue or pull request."""
+  MILESTONED_EVENT
+
+  """
+  Represents a 'moved_columns_in_project' event on a given issue or pull request.
+  """
+  MOVED_COLUMNS_IN_PROJECT_EVENT
+
+  """Represents a 'pinned' event on a given issue or pull request."""
+  PINNED_EVENT
+
+  """Represents a 'referenced' event on a given `ReferencedSubject`."""
+  REFERENCED_EVENT
+
+  """
+  Represents a 'removed_from_project' event on a given issue or pull request.
+  """
+  REMOVED_FROM_PROJECT_EVENT
+
+  """Represents a 'renamed' event on a given issue or pull request"""
+  RENAMED_TITLE_EVENT
+
+  """Represents a 'reopened' event on any `Closable`."""
+  REOPENED_EVENT
+
+  """Represents a 'subscribed' event on a given `Subscribable`."""
+  SUBSCRIBED_EVENT
+
+  """Represents a 'transferred' event on a given issue or pull request."""
+  TRANSFERRED_EVENT
+
+  """Represents an 'unassigned' event on any assignable object."""
+  UNASSIGNED_EVENT
+
+  """Represents an 'unlabeled' event on a given issue or pull request."""
+  UNLABELED_EVENT
+
+  """Represents an 'unlocked' event on a given issue or pull request."""
+  UNLOCKED_EVENT
+
+  """Represents a 'user_blocked' event on a given user."""
+  USER_BLOCKED_EVENT
+
+  """Represents an 'unpinned' event on a given issue or pull request."""
+  UNPINNED_EVENT
+
+  """Represents an 'unsubscribed' event on a given `Subscribable`."""
+  UNSUBSCRIBED_EVENT
+}
+
+"""A team or user who has the ability to push to a protected branch."""
+type PushAllowance implements Node {
+  """The actor that can push."""
+  actor: PushAllowanceActor
+
+  """
+  Identifies the branch protection rule associated with the allowed user or team.
+  """
+  branchProtectionRule: BranchProtectionRule
+  id: ID!
+}
+
+"""Types that can be an actor."""
+union PushAllowanceActor = User | Team
+
+"""The connection type for PushAllowance."""
+type PushAllowanceConnection {
+  """A list of edges."""
+  edges: [PushAllowanceEdge]
+
+  """A list of nodes."""
+  nodes: [PushAllowance]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type PushAllowanceEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: PushAllowance
+}
+
+"""The query root of GitHub's GraphQL interface."""
+type Query {
+  """Look up a code of conduct by its key"""
+  codeOfConduct(
+    """The code of conduct's key"""
+    key: String!
+  ): CodeOfConduct
+
+  """Look up a code of conduct by its key"""
+  codesOfConduct: [CodeOfConduct]
+
+  """Look up an open source license by its key"""
+  license(
+    """The license's downcased SPDX ID"""
+    key: String!
+  ): License
+
+  """Return a list of known open source licenses"""
+  licenses: [License]!
+
+  """Get alphabetically sorted list of Marketplace categories"""
+  marketplaceCategories(
+    """Return only the specified categories."""
+    includeCategories: [String!]
+
+    """Exclude categories with no listings."""
+    excludeEmpty: Boolean
+
+    """Returns top level categories only, excluding any subcategories."""
+    excludeSubcategories: Boolean
+  ): [MarketplaceCategory!]!
+
+  """Look up a Marketplace category by its slug."""
+  marketplaceCategory(
+    """The URL slug of the category."""
+    slug: String!
+
+    """Also check topic aliases for the category slug"""
+    useTopicAliases: Boolean
+  ): MarketplaceCategory
+
+  """Look up a single Marketplace listing"""
+  marketplaceListing(
+    """
+    Select the listing that matches this slug. It's the short name of the listing used in its URL.
+    """
+    slug: String!
+  ): MarketplaceListing
+
+  """Look up Marketplace listings"""
+  marketplaceListings(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Select only listings with the given category."""
+    categorySlug: String
+
+    """Also check topic aliases for the category slug"""
+    useTopicAliases: Boolean
+
+    """
+    Select listings to which user has admin access. If omitted, listings visible to the
+    viewer are returned.
+
+    """
+    viewerCanAdmin: Boolean
+
+    """Select listings that can be administered by the specified user."""
+    adminId: ID
+
+    """Select listings for products owned by the specified organization."""
+    organizationId: ID
+
+    """
+    Select listings visible to the viewer even if they are not approved. If omitted or
+    false, only approved listings will be returned.
+
+    """
+    allStates: Boolean
+
+    """
+    Select the listings with these slugs, if they are visible to the viewer.
+    """
+    slugs: [String]
+
+    """
+    Select only listings where the primary category matches the given category slug.
+    """
+    primaryCategoryOnly: Boolean = false
+
+    """Select only listings that offer a free trial."""
+    withFreeTrialsOnly: Boolean = false
+  ): MarketplaceListingConnection!
+
+  """Return information about the GitHub instance"""
+  meta: GitHubMetadata!
+
+  """Fetches an object given its ID."""
+  node(
+    """ID of the object."""
+    id: ID!
+  ): Node
+
+  """Lookup nodes by a list of IDs."""
+  nodes(
+    """The list of node IDs."""
+    ids: [ID!]!
+  ): [Node]!
+
+  """Lookup a organization by login."""
+  organization(
+    """The organization's login."""
+    login: String!
+  ): Organization
+
+  """The client's rate limit information."""
+  rateLimit(
+    """If true, calculate the cost for the query without evaluating it"""
+    dryRun: Boolean = false
+  ): RateLimit
+
+  """
+  Hack to workaround https://github.com/facebook/relay/issues/112 re-exposing the root query object
+  """
+  relay: Query!
+
+  """Lookup a given repository by the owner and repository name."""
+  repository(
+    """The login field of a user or organization"""
+    owner: String!
+
+    """The name of the repository"""
+    name: String!
+  ): Repository
+
+  """
+  Lookup a repository owner (ie. either a User or an Organization) by login.
+  """
+  repositoryOwner(
+    """The username to lookup the owner by."""
+    login: String!
+  ): RepositoryOwner
+
+  """Lookup resource by a URL."""
+  resource(
+    """The URL."""
+    url: URI!
+  ): UniformResourceLocatable
+
+  """Perform a search across resources."""
+  search(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """The search string to look for."""
+    query: String!
+
+    """The types of search items to search within."""
+    type: SearchType!
+  ): SearchResultItemConnection!
+
+  """GitHub Security Advisories"""
+  securityAdvisories(
+    """Ordering options for the returned topics."""
+    orderBy: SecurityAdvisoryOrder
+
+    """Filter advisories by identifier, e.g. GHSA or CVE."""
+    identifier: SecurityAdvisoryIdentifierFilter
+
+    """Filter advisories to those published since a time in the past."""
+    publishedSince: DateTime
+
+    """Filter advisories to those updated since a time in the past."""
+    updatedSince: DateTime
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): SecurityAdvisoryConnection!
+
+  """Fetch a Security Advisory by its GHSA ID"""
+  securityAdvisory(
+    """GitHub Security Advisory ID."""
+    ghsaId: String!
+  ): SecurityAdvisory
+
+  """Software Vulnerabilities documented by GitHub Security Advisories"""
+  securityVulnerabilities(
+    """Ordering options for the returned topics."""
+    orderBy: SecurityVulnerabilityOrder
+
+    """An ecosystem to filter vulnerabilities by."""
+    ecosystem: SecurityAdvisoryEcosystem
+
+    """A package name to filter vulnerabilities by."""
+    package: String
+
+    """A list of severities to filter vulnerabilities by."""
+    severities: [SecurityAdvisorySeverity!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): SecurityVulnerabilityConnection!
+
+  """Look up a topic by name."""
+  topic(
+    """The topic's name."""
+    name: String!
+  ): Topic
+
+  """Lookup a user by login."""
+  user(
+    """The user's login."""
+    login: String!
+  ): User
+
+  """The currently authenticated user."""
+  viewer: User!
+}
+
+"""Represents the client's rate limit."""
+type RateLimit {
+  """The point cost for the current query counting against the rate limit."""
+  cost: Int!
+
+  """
+  The maximum number of points the client is permitted to consume in a 60 minute window.
+  """
+  limit: Int!
+
+  """The maximum number of nodes this query may return"""
+  nodeCount: Int!
+
+  """The number of points remaining in the current rate limit window."""
+  remaining: Int!
+
+  """
+  The time at which the current rate limit window resets in UTC epoch seconds.
+  """
+  resetAt: DateTime!
+}
+
+"""Represents a subject that can be reacted on."""
+interface Reactable {
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+
+  """A list of reactions grouped by content left on the subject."""
+  reactionGroups: [ReactionGroup!]
+
+  """A list of Reactions left on the Issue."""
+  reactions(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Allows filtering Reactions by emoji."""
+    content: ReactionContent
+
+    """Allows specifying the order in which reactions are returned."""
+    orderBy: ReactionOrder
+  ): ReactionConnection!
+
+  """Can user react to this subject"""
+  viewerCanReact: Boolean!
+}
+
+"""The connection type for User."""
+type ReactingUserConnection {
+  """A list of edges."""
+  edges: [ReactingUserEdge]
+
+  """A list of nodes."""
+  nodes: [User]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a user that's made a reaction."""
+type ReactingUserEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+  node: User!
+
+  """The moment when the user made the reaction."""
+  reactedAt: DateTime!
+}
+
+"""An emoji reaction to a particular piece of content."""
+type Reaction implements Node {
+  """Identifies the emoji reaction."""
+  content: ReactionContent!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+
+  """The reactable piece of content"""
+  reactable: Reactable!
+
+  """Identifies the user who created this reaction."""
+  user: User
+}
+
+"""A list of reactions that have been left on the subject."""
+type ReactionConnection {
+  """A list of edges."""
+  edges: [ReactionEdge]
+
+  """A list of nodes."""
+  nodes: [Reaction]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+
+  """
+  Whether or not the authenticated user has left a reaction on the subject.
+  """
+  viewerHasReacted: Boolean!
+}
+
+"""Emojis that can be attached to Issues, Pull Requests and Comments."""
+enum ReactionContent {
+  """Represents the 👍 emoji."""
+  THUMBS_UP
+
+  """Represents the 👎 emoji."""
+  THUMBS_DOWN
+
+  """Represents the 😄 emoji."""
+  LAUGH
+
+  """Represents the 🎉 emoji."""
+  HOORAY
+
+  """Represents the 😕 emoji."""
+  CONFUSED
+
+  """Represents the ❤️ emoji."""
+  HEART
+
+  """Represents the 🚀 emoji."""
+  ROCKET
+
+  """Represents the 👀 emoji."""
+  EYES
+}
+
+"""An edge in a connection."""
+type ReactionEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Reaction
+}
+
+"""A group of emoji reactions to a particular piece of content."""
+type ReactionGroup {
+  """Identifies the emoji reaction."""
+  content: ReactionContent!
+
+  """Identifies when the reaction was created."""
+  createdAt: DateTime
+
+  """The subject that was reacted to."""
+  subject: Reactable!
+
+  """
+  Users who have reacted to the reaction subject with the emotion represented by this reaction group
+  """
+  users(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ReactingUserConnection!
+
+  """
+  Whether or not the authenticated user has left a reaction on the subject.
+  """
+  viewerHasReacted: Boolean!
+}
+
+"""Ways in which lists of reactions can be ordered upon return."""
+input ReactionOrder {
+  """The field in which to order reactions by."""
+  field: ReactionOrderField!
+
+  """The direction in which to order reactions by the specified field."""
+  direction: OrderDirection!
+}
+
+"""A list of fields that reactions can be ordered by."""
+enum ReactionOrderField {
+  """Allows ordering a list of reactions by when they were created."""
+  CREATED_AT
+}
+
+"""Represents a Git reference."""
+type Ref implements Node {
+  """A list of pull requests with this ref as the head ref."""
+  associatedPullRequests(
+    """A list of states to filter the pull requests by."""
+    states: [PullRequestState!]
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """The head ref name to filter the pull requests by."""
+    headRefName: String
+
+    """The base ref name to filter the pull requests by."""
+    baseRefName: String
+
+    """Ordering options for pull requests returned from the connection."""
+    orderBy: IssueOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestConnection!
+  id: ID!
+
+  """The ref name."""
+  name: String!
+
+  """The ref's prefix, such as `refs/heads/` or `refs/tags/`."""
+  prefix: String!
+
+  """The repository the ref belongs to."""
+  repository: Repository!
+
+  """The object the ref points to."""
+  target: GitObject!
+}
+
+"""The connection type for Ref."""
+type RefConnection {
+  """A list of edges."""
+  edges: [RefEdge]
+
+  """A list of nodes."""
+  nodes: [Ref]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type RefEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Ref
+}
+
+"""Represents a 'referenced' event on a given `ReferencedSubject`."""
+type ReferencedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the commit associated with the 'referenced' event."""
+  commit: Commit
+
+  """Identifies the repository associated with the 'referenced' event."""
+  commitRepository: Repository!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Reference originated in a different repository."""
+  isCrossRepository: Boolean!
+
+  """
+  Checks if the commit message itself references the subject. Can be false in the case of a commit comment reference.
+  """
+  isDirectReference: Boolean!
+
+  """Object referenced by event."""
+  subject: ReferencedSubject!
+}
+
+"""Any referencable object"""
+union ReferencedSubject = Issue | PullRequest
+
+"""Ways in which lists of git refs can be ordered upon return."""
+input RefOrder {
+  """The field in which to order refs by."""
+  field: RefOrderField!
+
+  """The direction in which to order refs by the specified field."""
+  direction: OrderDirection!
+}
+
+"""Properties by which ref connections can be ordered."""
+enum RefOrderField {
+  """Order refs by underlying commit date if the ref prefix is refs/tags/"""
+  TAG_COMMIT_DATE
+
+  """Order refs by their alphanumeric name"""
+  ALPHABETICAL
+}
+
+"""Represents an owner of a registry package."""
+interface RegistryPackageOwner {
+  id: ID!
+}
+
+"""Represents an interface to search packages on an object."""
+interface RegistryPackageSearch {
+  id: ID!
+}
+
+"""A release contains the content for a release."""
+type Release implements Node & UniformResourceLocatable {
+  """The author of the release"""
+  author: User
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the description of the release."""
+  description: String
+  id: ID!
+
+  """Whether or not the release is a draft"""
+  isDraft: Boolean!
+
+  """Whether or not the release is a prerelease"""
+  isPrerelease: Boolean!
+
+  """Identifies the title of the release."""
+  name: String
+
+  """Identifies the date and time when the release was created."""
+  publishedAt: DateTime
+
+  """List of releases assets which are dependent on this release."""
+  releaseAssets(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """A list of names to filter the assets by."""
+    name: String
+  ): ReleaseAssetConnection!
+
+  """The HTTP path for this issue"""
+  resourcePath: URI!
+
+  """The Git tag the release points to"""
+  tag: Ref
+
+  """The name of the release's Git tag"""
+  tagName: String!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this issue"""
+  url: URI!
+}
+
+"""A release asset contains the content for a release asset."""
+type ReleaseAsset implements Node {
+  """The asset's content-type"""
+  contentType: String!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The number of times this asset was downloaded"""
+  downloadCount: Int!
+
+  """
+  Identifies the URL where you can download the release asset via the browser.
+  """
+  downloadUrl: URI!
+  id: ID!
+
+  """Identifies the title of the release asset."""
+  name: String!
+
+  """Release that the asset is associated with"""
+  release: Release
+
+  """The size (in bytes) of the asset"""
+  size: Int!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The user that performed the upload"""
+  uploadedBy: User!
+
+  """Identifies the URL of the release asset."""
+  url: URI!
+}
+
+"""The connection type for ReleaseAsset."""
+type ReleaseAssetConnection {
+  """A list of edges."""
+  edges: [ReleaseAssetEdge]
+
+  """A list of nodes."""
+  nodes: [ReleaseAsset]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type ReleaseAssetEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: ReleaseAsset
+}
+
+"""The connection type for Release."""
+type ReleaseConnection {
+  """A list of edges."""
+  edges: [ReleaseEdge]
+
+  """A list of nodes."""
+  nodes: [Release]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type ReleaseEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Release
+}
+
+"""Ways in which lists of releases can be ordered upon return."""
+input ReleaseOrder {
+  """The field in which to order releases by."""
+  field: ReleaseOrderField!
+
+  """The direction in which to order releases by the specified field."""
+  direction: OrderDirection!
+}
+
+"""Properties by which release connections can be ordered."""
+enum ReleaseOrderField {
+  """Order releases by creation time"""
+  CREATED_AT
+
+  """Order releases alphabetically by name"""
+  NAME
+}
+
+"""Autogenerated input type of RemoveAssigneesFromAssignable"""
+input RemoveAssigneesFromAssignableInput {
+  """The id of the assignable object to remove assignees from."""
+  assignableId: ID!
+
+  """The id of users to remove as assignees."""
+  assigneeIds: [ID!]!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of RemoveAssigneesFromAssignable"""
+type RemoveAssigneesFromAssignablePayload {
+  """The item that was unassigned."""
+  assignable: Assignable
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""
+Represents a 'removed_from_project' event on a given issue or pull request.
+"""
+type RemovedFromProjectEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+}
+
+"""Autogenerated input type of RemoveLabelsFromLabelable"""
+input RemoveLabelsFromLabelableInput {
+  """The id of the Labelable to remove labels from."""
+  labelableId: ID!
+
+  """The ids of labels to remove."""
+  labelIds: [ID!]!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of RemoveLabelsFromLabelable"""
+type RemoveLabelsFromLabelablePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The Labelable the labels were removed from."""
+  labelable: Labelable
+}
+
+"""Autogenerated input type of RemoveOutsideCollaborator"""
+input RemoveOutsideCollaboratorInput {
+  """The ID of the outside collaborator to remove."""
+  userId: ID!
+
+  """The ID of the organization to remove the outside collaborator from."""
+  organizationId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of RemoveOutsideCollaborator"""
+type RemoveOutsideCollaboratorPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The user that was removed as an outside collaborator."""
+  removedUser: User
+}
+
+"""Autogenerated input type of RemoveReaction"""
+input RemoveReactionInput {
+  """The Node ID of the subject to modify."""
+  subjectId: ID!
+
+  """The name of the emoji reaction to remove."""
+  content: ReactionContent!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of RemoveReaction"""
+type RemoveReactionPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The reaction object."""
+  reaction: Reaction
+
+  """The reactable subject."""
+  subject: Reactable
+}
+
+"""Autogenerated input type of RemoveStar"""
+input RemoveStarInput {
+  """The Starrable ID to unstar."""
+  starrableId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of RemoveStar"""
+type RemoveStarPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The starrable."""
+  starrable: Starrable
+}
+
+"""Represents a 'renamed' event on a given issue or pull request"""
+type RenamedTitleEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the current title of the issue or pull request."""
+  currentTitle: String!
+  id: ID!
+
+  """Identifies the previous title of the issue or pull request."""
+  previousTitle: String!
+
+  """Subject that was renamed."""
+  subject: RenamedTitleSubject!
+}
+
+"""An object which has a renamable title"""
+union RenamedTitleSubject = Issue | PullRequest
+
+"""Represents a 'reopened' event on any `Closable`."""
+type ReopenedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Object that was reopened."""
+  closable: Closable!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+}
+
+"""Autogenerated input type of ReopenIssue"""
+input ReopenIssueInput {
+  """ID of the issue to be opened."""
+  issueId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of ReopenIssue"""
+type ReopenIssuePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The issue that was opened."""
+  issue: Issue
+}
+
+"""Autogenerated input type of ReopenPullRequest"""
+input ReopenPullRequestInput {
+  """ID of the pull request to be reopened."""
+  pullRequestId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of ReopenPullRequest"""
+type ReopenPullRequestPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The pull request that was reopened."""
+  pullRequest: PullRequest
+}
+
+"""The reasons a piece of content can be reported or minimized."""
+enum ReportedContentClassifiers {
+  """A spammy piece of content"""
+  SPAM
+
+  """An abusive or harassing piece of content"""
+  ABUSE
+
+  """An irrelevant piece of content"""
+  OFF_TOPIC
+
+  """An outdated piece of content"""
+  OUTDATED
+
+  """The content has been resolved"""
+  RESOLVED
+}
+
+"""A repository contains the content for a project."""
+type Repository implements Node & ProjectOwner & RegistryPackageOwner & Subscribable & Starrable & UniformResourceLocatable & RepositoryInfo {
+  """A list of users that can be assigned to issues in this repository."""
+  assignableUsers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+
+  """A list of branch protection rules for this repository."""
+  branchProtectionRules(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): BranchProtectionRuleConnection!
+
+  """Returns the code of conduct for this repository"""
+  codeOfConduct: CodeOfConduct
+
+  """A list of collaborators associated with the repository."""
+  collaborators(
+    """Collaborators affiliation level with a repository."""
+    affiliation: CollaboratorAffiliation
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RepositoryCollaboratorConnection
+
+  """A list of commit comments associated with the repository."""
+  commitComments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): CommitCommentConnection!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The Ref associated with the repository's default branch."""
+  defaultBranchRef: Ref
+
+  """A list of deploy keys that are on this repository."""
+  deployKeys(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): DeployKeyConnection!
+
+  """Deployments associated with the repository"""
+  deployments(
+    """Environments to list deployments for"""
+    environments: [String!]
+
+    """Ordering options for deployments returned from the connection."""
+    orderBy: DeploymentOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): DeploymentConnection!
+
+  """The description of the repository."""
+  description: String
+
+  """The description of the repository rendered to HTML."""
+  descriptionHTML: HTML!
+
+  """The number of kilobytes this repository occupies on disk."""
+  diskUsage: Int
+
+  """
+  Returns how many forks there are of this repository in the whole network.
+  """
+  forkCount: Int!
+
+  """A list of direct forked repositories."""
+  forks(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """
+    Array of viewer's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    current viewer owns.
+    """
+    affiliations: [RepositoryAffiliation]
+
+    """
+    Array of owner's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    organization or user being viewed owns.
+    """
+    ownerAffiliations: [RepositoryAffiliation]
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RepositoryConnection!
+
+  """Indicates if the repository has issues feature enabled."""
+  hasIssuesEnabled: Boolean!
+
+  """Indicates if the repository has wiki feature enabled."""
+  hasWikiEnabled: Boolean!
+
+  """The repository's URL."""
+  homepageUrl: URI
+  id: ID!
+
+  """Indicates if the repository is unmaintained."""
+  isArchived: Boolean!
+
+  """Returns whether or not this repository disabled."""
+  isDisabled: Boolean!
+
+  """Identifies if the repository is a fork."""
+  isFork: Boolean!
+
+  """Indicates if the repository has been locked or not."""
+  isLocked: Boolean!
+
+  """Identifies if the repository is a mirror."""
+  isMirror: Boolean!
+
+  """Identifies if the repository is private."""
+  isPrivate: Boolean!
+
+  """Returns a single issue from the current repository by number."""
+  issue(
+    """The number for the issue to be returned."""
+    number: Int!
+  ): Issue
+
+  """
+  Returns a single issue-like object from the current repository by number.
+  """
+  issueOrPullRequest(
+    """The number for the issue to be returned."""
+    number: Int!
+  ): IssueOrPullRequest
+
+  """A list of issues that have been opened in the repository."""
+  issues(
+    """Ordering options for issues returned from the connection."""
+    orderBy: IssueOrder
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """A list of states to filter the issues by."""
+    states: [IssueState!]
+
+    """Filtering options for issues returned from the connection."""
+    filterBy: IssueFilters
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueConnection!
+
+  """Returns a single label by name"""
+  label(
+    """Label name"""
+    name: String!
+  ): Label
+
+  """A list of labels associated with the repository."""
+  labels(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """If provided, searches labels by name and description."""
+    query: String
+  ): LabelConnection
+
+  """
+  A list containing a breakdown of the language composition of the repository.
+  """
+  languages(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Order for connection"""
+    orderBy: LanguageOrder
+  ): LanguageConnection
+
+  """The license associated with the repository"""
+  licenseInfo: License
+
+  """The reason the repository has been locked."""
+  lockReason: RepositoryLockReason
+
+  """
+  A list of Users that can be mentioned in the context of the repository.
+  """
+  mentionableUsers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+
+  """Whether or not PRs are merged with a merge commit on this repository."""
+  mergeCommitAllowed: Boolean!
+
+  """Returns a single milestone from the current repository by number."""
+  milestone(
+    """The number for the milestone to be returned."""
+    number: Int!
+  ): Milestone
+
+  """A list of milestones associated with the repository."""
+  milestones(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Filter by the state of the milestones."""
+    states: [MilestoneState!]
+
+    """Ordering options for milestones."""
+    orderBy: MilestoneOrder
+  ): MilestoneConnection
+
+  """The repository's original mirror URL."""
+  mirrorUrl: URI
+
+  """The name of the repository."""
+  name: String!
+
+  """The repository's name with owner."""
+  nameWithOwner: String!
+
+  """A Git object in the repository"""
+  object(
+    """The Git object ID"""
+    oid: GitObjectID
+
+    """A Git revision expression suitable for rev-parse"""
+    expression: String
+  ): GitObject
+
+  """The User owner of the repository."""
+  owner: RepositoryOwner!
+
+  """The repository parent, if this is a fork."""
+  parent: Repository
+
+  """The primary language of the repository's code."""
+  primaryLanguage: Language
+
+  """Find project by number."""
+  project(
+    """The project number to find."""
+    number: Int!
+  ): Project
+
+  """A list of projects under the owner."""
+  projects(
+    """Ordering options for projects returned from the connection"""
+    orderBy: ProjectOrder
+
+    """Query to search projects by, currently only searching by name."""
+    search: String
+
+    """A list of states to filter the projects by."""
+    states: [ProjectState!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ProjectConnection!
+
+  """The HTTP path listing the repository's projects"""
+  projectsResourcePath: URI!
+
+  """The HTTP URL listing the repository's projects"""
+  projectsUrl: URI!
+
+  """Returns a single pull request from the current repository by number."""
+  pullRequest(
+    """The number for the pull request to be returned."""
+    number: Int!
+  ): PullRequest
+
+  """A list of pull requests that have been opened in the repository."""
+  pullRequests(
+    """A list of states to filter the pull requests by."""
+    states: [PullRequestState!]
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """The head ref name to filter the pull requests by."""
+    headRefName: String
+
+    """The base ref name to filter the pull requests by."""
+    baseRefName: String
+
+    """Ordering options for pull requests returned from the connection."""
+    orderBy: IssueOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestConnection!
+
+  """Identifies when the repository was last pushed to."""
+  pushedAt: DateTime
+
+  """Whether or not rebase-merging is enabled on this repository."""
+  rebaseMergeAllowed: Boolean!
+
+  """Fetch a given ref from the repository"""
+  ref(
+    """
+    The ref to retrieve. Fully qualified matches are checked in order
+    (`refs/heads/master`) before falling back onto checks for short name matches (`master`).
+    """
+    qualifiedName: String!
+  ): Ref
+
+  """Fetch a list of refs from the repository"""
+  refs(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """A ref name prefix like `refs/heads/`, `refs/tags/`, etc."""
+    refPrefix: String!
+
+    """DEPRECATED: use orderBy. The ordering direction."""
+    direction: OrderDirection
+
+    """Ordering options for refs returned from the connection."""
+    orderBy: RefOrder
+  ): RefConnection
+
+  """Lookup a single release given various criteria."""
+  release(
+    """The name of the Tag the Release was created from"""
+    tagName: String!
+  ): Release
+
+  """List of releases which are dependent on this repository."""
+  releases(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Order for connection"""
+    orderBy: ReleaseOrder
+  ): ReleaseConnection!
+
+  """A list of applied repository-topic associations for this repository."""
+  repositoryTopics(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RepositoryTopicConnection!
+
+  """The HTTP path for this repository"""
+  resourcePath: URI!
+
+  """
+  A description of the repository, rendered to HTML without any links in it.
+  """
+  shortDescriptionHTML(
+    """How many characters to return."""
+    limit: Int = 200
+  ): HTML!
+
+  """Whether or not squash-merging is enabled on this repository."""
+  squashMergeAllowed: Boolean!
+
+  """The SSH URL to clone this repository"""
+  sshUrl: GitSSHRemote!
+
+  """A list of users who have starred this starrable."""
+  stargazers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Order for connection"""
+    orderBy: StarOrder
+  ): StargazerConnection!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this repository"""
+  url: URI!
+
+  """Indicates whether the viewer has admin permissions on this repository."""
+  viewerCanAdminister: Boolean!
+
+  """Can the current viewer create new projects on this owner."""
+  viewerCanCreateProjects: Boolean!
+
+  """
+  Check if the viewer is able to change their subscription status for the repository.
+  """
+  viewerCanSubscribe: Boolean!
+
+  """Indicates whether the viewer can update the topics of this repository."""
+  viewerCanUpdateTopics: Boolean!
+
+  """
+  Returns a boolean indicating whether the viewing user has starred this starrable.
+  """
+  viewerHasStarred: Boolean!
+
+  """
+  The users permission level on the repository. Will return null if authenticated as an GitHub App.
+  """
+  viewerPermission: RepositoryPermission
+
+  """
+  Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+  """
+  viewerSubscription: SubscriptionState
+
+  """A list of users watching the repository."""
+  watchers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): UserConnection!
+}
+
+"""The affiliation of a user to a repository"""
+enum RepositoryAffiliation {
+  """Repositories that are owned by the authenticated user."""
+  OWNER
+
+  """Repositories that the user has been added to as a collaborator."""
+  COLLABORATOR
+
+  """
+  Repositories that the user has access to through being a member of an
+  organization. This includes every repository on every team that the user is on.
+  """
+  ORGANIZATION_MEMBER
+}
+
+"""The affiliation type between collaborator and repository."""
+enum RepositoryCollaboratorAffiliation {
+  """All collaborators of the repository."""
+  ALL
+
+  """All outside collaborators of an organization-owned repository."""
+  OUTSIDE
+}
+
+"""The connection type for User."""
+type RepositoryCollaboratorConnection {
+  """A list of edges."""
+  edges: [RepositoryCollaboratorEdge]
+
+  """A list of nodes."""
+  nodes: [User]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a user who is a collaborator of a repository."""
+type RepositoryCollaboratorEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+  node: User!
+
+  """The permission the user has on the repository."""
+  permission: RepositoryPermission!
+
+  """A list of sources for the user's access to the repository."""
+  permissionSources: [PermissionSource!]
+}
+
+"""A list of repositories owned by the subject."""
+type RepositoryConnection {
+  """A list of edges."""
+  edges: [RepositoryEdge]
+
+  """A list of nodes."""
+  nodes: [Repository]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+
+  """The total size in kilobytes of all repositories in the connection."""
+  totalDiskUsage: Int!
+}
+
+"""The reason a repository is listed as 'contributed'."""
+enum RepositoryContributionType {
+  """Created a commit"""
+  COMMIT
+
+  """Created an issue"""
+  ISSUE
+
+  """Created a pull request"""
+  PULL_REQUEST
+
+  """Created the repository"""
+  REPOSITORY
+
+  """Reviewed a pull request"""
+  PULL_REQUEST_REVIEW
+}
+
+"""An edge in a connection."""
+type RepositoryEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Repository
+}
+
+"""A subset of repository info."""
+interface RepositoryInfo {
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The description of the repository."""
+  description: String
+
+  """The description of the repository rendered to HTML."""
+  descriptionHTML: HTML!
+
+  """
+  Returns how many forks there are of this repository in the whole network.
+  """
+  forkCount: Int!
+
+  """Indicates if the repository has issues feature enabled."""
+  hasIssuesEnabled: Boolean!
+
+  """Indicates if the repository has wiki feature enabled."""
+  hasWikiEnabled: Boolean!
+
+  """The repository's URL."""
+  homepageUrl: URI
+
+  """Indicates if the repository is unmaintained."""
+  isArchived: Boolean!
+
+  """Identifies if the repository is a fork."""
+  isFork: Boolean!
+
+  """Indicates if the repository has been locked or not."""
+  isLocked: Boolean!
+
+  """Identifies if the repository is a mirror."""
+  isMirror: Boolean!
+
+  """Identifies if the repository is private."""
+  isPrivate: Boolean!
+
+  """The license associated with the repository"""
+  licenseInfo: License
+
+  """The reason the repository has been locked."""
+  lockReason: RepositoryLockReason
+
+  """The repository's original mirror URL."""
+  mirrorUrl: URI
+
+  """The name of the repository."""
+  name: String!
+
+  """The repository's name with owner."""
+  nameWithOwner: String!
+
+  """The User owner of the repository."""
+  owner: RepositoryOwner!
+
+  """Identifies when the repository was last pushed to."""
+  pushedAt: DateTime
+
+  """The HTTP path for this repository"""
+  resourcePath: URI!
+
+  """
+  A description of the repository, rendered to HTML without any links in it.
+  """
+  shortDescriptionHTML(
+    """How many characters to return."""
+    limit: Int = 200
+  ): HTML!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this repository"""
+  url: URI!
+}
+
+"""An invitation for a user to be added to a repository."""
+type RepositoryInvitation implements Node {
+  id: ID!
+
+  """The user who received the invitation."""
+  invitee: User!
+
+  """The user who created the invitation."""
+  inviter: User!
+
+  """The permission granted on this repository by this invitation."""
+  permission: RepositoryPermission!
+
+  """The Repository the user is invited to."""
+  repository: RepositoryInfo
+}
+
+"""An edge in a connection."""
+type RepositoryInvitationEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: RepositoryInvitation
+}
+
+"""The possible reasons a given repository could be in a locked state."""
+enum RepositoryLockReason {
+  """The repository is locked due to a move."""
+  MOVING
+
+  """The repository is locked due to a billing related reason."""
+  BILLING
+
+  """The repository is locked due to a rename."""
+  RENAME
+
+  """The repository is locked due to a migration."""
+  MIGRATING
+}
+
+"""Represents a object that belongs to a repository."""
+interface RepositoryNode {
+  """The repository associated with this node."""
+  repository: Repository!
+}
+
+"""Ordering options for repository connections"""
+input RepositoryOrder {
+  """The field to order repositories by."""
+  field: RepositoryOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which repository connections can be ordered."""
+enum RepositoryOrderField {
+  """Order repositories by creation time"""
+  CREATED_AT
+
+  """Order repositories by update time"""
+  UPDATED_AT
+
+  """Order repositories by push time"""
+  PUSHED_AT
+
+  """Order repositories by name"""
+  NAME
+
+  """Order repositories by number of stargazers"""
+  STARGAZERS
+}
+
+"""Represents an owner of a Repository."""
+interface RepositoryOwner {
+  """A URL pointing to the owner's public avatar."""
+  avatarUrl(
+    """The size of the resulting square image."""
+    size: Int
+  ): URI!
+  id: ID!
+
+  """The username used to login."""
+  login: String!
+
+  """A list of repositories this user has pinned to their profile"""
+  pinnedRepositories(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """
+    Array of viewer's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    current viewer owns.
+    """
+    affiliations: [RepositoryAffiliation]
+
+    """
+    Array of owner's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    organization or user being viewed owns.
+    """
+    ownerAffiliations: [RepositoryAffiliation]
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RepositoryConnection! @deprecated(reason: "pinnedRepositories will be removed Use ProfileOwner.pinnedItems instead. Removal on 2019-07-01 UTC.")
+
+  """A list of repositories that the user owns."""
+  repositories(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """
+    Array of viewer's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    current viewer owns.
+    """
+    affiliations: [RepositoryAffiliation]
+
+    """
+    Array of owner's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    organization or user being viewed owns.
+    """
+    ownerAffiliations: [RepositoryAffiliation]
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """
+    If non-null, filters repositories according to whether they are forks of another repository
+    """
+    isFork: Boolean
+  ): RepositoryConnection!
+
+  """Find Repository."""
+  repository(
+    """Name of Repository to find."""
+    name: String!
+  ): Repository
+
+  """The HTTP URL for the owner."""
+  resourcePath: URI!
+
+  """The HTTP URL for the owner."""
+  url: URI!
+}
+
+"""The access level to a repository"""
+enum RepositoryPermission {
+  """Can read, clone, push, and add collaborators"""
+  ADMIN
+
+  """Can read, clone and push"""
+  WRITE
+
+  """Can read and clone"""
+  READ
+}
+
+"""The privacy of a repository"""
+enum RepositoryPrivacy {
+  """Public"""
+  PUBLIC
+
+  """Private"""
+  PRIVATE
+}
+
+"""A repository-topic connects a repository to a topic."""
+type RepositoryTopic implements Node & UniformResourceLocatable {
+  id: ID!
+
+  """The HTTP path for this repository-topic."""
+  resourcePath: URI!
+
+  """The topic."""
+  topic: Topic!
+
+  """The HTTP URL for this repository-topic."""
+  url: URI!
+}
+
+"""The connection type for RepositoryTopic."""
+type RepositoryTopicConnection {
+  """A list of edges."""
+  edges: [RepositoryTopicEdge]
+
+  """A list of nodes."""
+  nodes: [RepositoryTopic]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type RepositoryTopicEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: RepositoryTopic
+}
+
+"""Types that can be requested reviewers."""
+union RequestedReviewer = User | Team | Mannequin
+
+"""Autogenerated input type of RequestReviews"""
+input RequestReviewsInput {
+  """The Node ID of the pull request to modify."""
+  pullRequestId: ID!
+
+  """The Node IDs of the user to request."""
+  userIds: [ID!]
+
+  """The Node IDs of the team to request."""
+  teamIds: [ID!]
+
+  """Add users to the set rather than replace."""
+  union: Boolean
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of RequestReviews"""
+type RequestReviewsPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The pull request that is getting requests."""
+  pullRequest: PullRequest
+
+  """The edge from the pull request to the requested reviewers."""
+  requestedReviewersEdge: UserEdge
+}
+
+"""Autogenerated input type of ResolveReviewThread"""
+input ResolveReviewThreadInput {
+  """The ID of the thread to resolve"""
+  threadId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of ResolveReviewThread"""
+type ResolveReviewThreadPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The thread to resolve."""
+  thread: PullRequestReviewThread
+}
+
+"""Represents a private contribution a user made on GitHub."""
+type RestrictedContribution implements Contribution {
+  """
+  Whether this contribution is associated with a record you do not have access to. For
+  example, your own 'first issue' contribution may have been made on a repository you can no
+  longer access.
+
+  """
+  isRestricted: Boolean!
+
+  """When this contribution was made."""
+  occurredAt: DateTime!
+
+  """The HTTP path for this contribution."""
+  resourcePath: URI!
+
+  """The HTTP URL for this contribution."""
+  url: URI!
+
+  """
+  The user who made this contribution.
+
+  """
+  user: User!
+}
+
+"""
+A team or user who has the ability to dismiss a review on a protected branch.
+"""
+type ReviewDismissalAllowance implements Node {
+  """The actor that can dismiss."""
+  actor: ReviewDismissalAllowanceActor
+
+  """
+  Identifies the branch protection rule associated with the allowed user or team.
+  """
+  branchProtectionRule: BranchProtectionRule
+  id: ID!
+}
+
+"""Types that can be an actor."""
+union ReviewDismissalAllowanceActor = User | Team
+
+"""The connection type for ReviewDismissalAllowance."""
+type ReviewDismissalAllowanceConnection {
+  """A list of edges."""
+  edges: [ReviewDismissalAllowanceEdge]
+
+  """A list of nodes."""
+  nodes: [ReviewDismissalAllowance]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type ReviewDismissalAllowanceEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: ReviewDismissalAllowance
+}
+
+"""
+Represents a 'review_dismissed' event on a given issue or pull request.
+"""
+type ReviewDismissedEvent implements Node & UniformResourceLocatable {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """
+  Identifies the optional message associated with the 'review_dismissed' event.
+  """
+  dismissalMessage: String
+
+  """
+  Identifies the optional message associated with the event, rendered to HTML.
+  """
+  dismissalMessageHTML: String
+  id: ID!
+
+  """Identifies the message associated with the 'review_dismissed' event."""
+  message: String! @deprecated(reason: "`message` is being removed because it not nullable, whereas the underlying field is optional. Use `dismissalMessage` instead. Removal on 2019-07-01 UTC.")
+
+  """The message associated with the event, rendered to HTML."""
+  messageHtml: HTML! @deprecated(reason: "`messageHtml` is being removed because it not nullable, whereas the underlying field is optional. Use `dismissalMessageHTML` instead. Removal on 2019-07-01 UTC.")
+
+  """
+  Identifies the previous state of the review with the 'review_dismissed' event.
+  """
+  previousReviewState: PullRequestReviewState!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+
+  """Identifies the commit which caused the review to become stale."""
+  pullRequestCommit: PullRequestCommit
+
+  """The HTTP path for this review dismissed event."""
+  resourcePath: URI!
+
+  """Identifies the review associated with the 'review_dismissed' event."""
+  review: PullRequestReview
+
+  """The HTTP URL for this review dismissed event."""
+  url: URI!
+}
+
+"""A request for a user to review a pull request."""
+type ReviewRequest implements Node {
+  """Identifies the primary key from the database."""
+  databaseId: Int
+  id: ID!
+
+  """Identifies the pull request associated with this review request."""
+  pullRequest: PullRequest!
+
+  """The reviewer that is requested."""
+  requestedReviewer: RequestedReviewer
+}
+
+"""The connection type for ReviewRequest."""
+type ReviewRequestConnection {
+  """A list of edges."""
+  edges: [ReviewRequestEdge]
+
+  """A list of nodes."""
+  nodes: [ReviewRequest]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents an 'review_requested' event on a given pull request."""
+type ReviewRequestedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+
+  """Identifies the reviewer whose review was requested."""
+  requestedReviewer: RequestedReviewer
+}
+
+"""An edge in a connection."""
+type ReviewRequestEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: ReviewRequest
+}
+
+"""Represents an 'review_request_removed' event on a given pull request."""
+type ReviewRequestRemovedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """PullRequest referenced by event."""
+  pullRequest: PullRequest!
+
+  """Identifies the reviewer whose review request was removed."""
+  requestedReviewer: RequestedReviewer
+}
+
+"""The results of a search."""
+union SearchResultItem = Issue | PullRequest | Repository | User | Organization | MarketplaceListing
+
+"""A list of results that matched against a search query."""
+type SearchResultItemConnection {
+  """The number of pieces of code that matched the search query."""
+  codeCount: Int!
+
+  """A list of edges."""
+  edges: [SearchResultItemEdge]
+
+  """The number of issues that matched the search query."""
+  issueCount: Int!
+
+  """A list of nodes."""
+  nodes: [SearchResultItem]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """The number of repositories that matched the search query."""
+  repositoryCount: Int!
+
+  """The number of users that matched the search query."""
+  userCount: Int!
+
+  """The number of wiki pages that matched the search query."""
+  wikiCount: Int!
+}
+
+"""An edge in a connection."""
+type SearchResultItemEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: SearchResultItem
+
+  """Text matches on the result found."""
+  textMatches: [TextMatch]
+}
+
+"""Represents the individual results of a search."""
+enum SearchType {
+  """Returns results matching issues in repositories."""
+  ISSUE
+
+  """Returns results matching repositories."""
+  REPOSITORY
+
+  """Returns results matching users and organizations on GitHub."""
+  USER
+}
+
+"""A GitHub Security Advisory"""
+type SecurityAdvisory implements Node {
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """This is a long plaintext description of the advisory"""
+  description: String!
+
+  """The GitHub Security Advisory ID"""
+  ghsaId: String!
+  id: ID!
+
+  """A list of identifiers for this advisory"""
+  identifiers: [SecurityAdvisoryIdentifier!]!
+
+  """The organization that originated the advisory"""
+  origin: String!
+
+  """When the advisory was published"""
+  publishedAt: DateTime!
+
+  """A list of references for this advisory"""
+  references: [SecurityAdvisoryReference!]!
+
+  """The severity of the advisory"""
+  severity: SecurityAdvisorySeverity!
+
+  """A short plaintext summary of the advisory"""
+  summary: String!
+
+  """When the advisory was last updated"""
+  updatedAt: DateTime!
+
+  """Vulnerabilities associated with this Advisory"""
+  vulnerabilities(
+    """Ordering options for the returned topics."""
+    orderBy: SecurityVulnerabilityOrder
+
+    """An ecosystem to filter vulnerabilities by."""
+    ecosystem: SecurityAdvisoryEcosystem
+
+    """A package name to filter vulnerabilities by."""
+    package: String
+
+    """A list of severities to filter vulnerabilities by."""
+    severities: [SecurityAdvisorySeverity!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): SecurityVulnerabilityConnection!
+
+  """When the advisory was withdrawn, if it has been withdrawn"""
+  withdrawnAt: DateTime
+}
+
+"""The connection type for SecurityAdvisory."""
+type SecurityAdvisoryConnection {
+  """A list of edges."""
+  edges: [SecurityAdvisoryEdge]
+
+  """A list of nodes."""
+  nodes: [SecurityAdvisory]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""The possible ecosystems of a security vulnerability's package."""
+enum SecurityAdvisoryEcosystem {
+  """Ruby gems hosted at RubyGems.org"""
+  RUBYGEMS
+
+  """JavaScript packages hosted at npmjs.com"""
+  NPM
+
+  """Python packages hosted at PyPI.org"""
+  PIP
+
+  """Java artifacts hosted at the Maven central repository"""
+  MAVEN
+
+  """.NET packages hosted at the NuGet Gallery"""
+  NUGET
+}
+
+"""An edge in a connection."""
+type SecurityAdvisoryEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: SecurityAdvisory
+}
+
+"""A GitHub Security Advisory Identifier"""
+type SecurityAdvisoryIdentifier {
+  """The identifier type, e.g. GHSA, CVE"""
+  type: String!
+
+  """The identifier"""
+  value: String!
+}
+
+"""An advisory identifier to filter results on."""
+input SecurityAdvisoryIdentifierFilter {
+  """The identifier type."""
+  type: SecurityAdvisoryIdentifierType!
+
+  """The identifier string. Supports exact or partial matching."""
+  value: String!
+}
+
+"""Identifier formats available for advisories."""
+enum SecurityAdvisoryIdentifierType {
+  """Common Vulnerabilities and Exposures Identifier."""
+  CVE
+
+  """GitHub Security Advisory ID."""
+  GHSA
+}
+
+"""Ordering options for security advisory connections"""
+input SecurityAdvisoryOrder {
+  """The field to order security advisories by."""
+  field: SecurityAdvisoryOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which security advisory connections can be ordered."""
+enum SecurityAdvisoryOrderField {
+  """Order advisories by publication time"""
+  PUBLISHED_AT
+
+  """Order advisories by update time"""
+  UPDATED_AT
+}
+
+"""An individual package"""
+type SecurityAdvisoryPackage {
+  """The ecosystem the package belongs to, e.g. RUBYGEMS, NPM"""
+  ecosystem: SecurityAdvisoryEcosystem!
+
+  """The package name"""
+  name: String!
+}
+
+"""An individual package version"""
+type SecurityAdvisoryPackageVersion {
+  """The package name or version"""
+  identifier: String!
+}
+
+"""A GitHub Security Advisory Reference"""
+type SecurityAdvisoryReference {
+  """A publicly accessible reference"""
+  url: URI!
+}
+
+"""Severity of the vulnerability."""
+enum SecurityAdvisorySeverity {
+  """Low."""
+  LOW
+
+  """Moderate."""
+  MODERATE
+
+  """High."""
+  HIGH
+
+  """Critical."""
+  CRITICAL
+}
+
+"""An individual vulnerability within an Advisory"""
+type SecurityVulnerability {
+  """The Advisory associated with this Vulnerability"""
+  advisory: SecurityAdvisory!
+
+  """The first version containing a fix for the vulnerability"""
+  firstPatchedVersion: SecurityAdvisoryPackageVersion
+
+  """A description of the vulnerable package"""
+  package: SecurityAdvisoryPackage!
+
+  """The severity of the vulnerability within this package"""
+  severity: SecurityAdvisorySeverity!
+
+  """When the vulnerability was last updated"""
+  updatedAt: DateTime!
+
+  """
+  A string that describes the vulnerable package versions.
+  This string follows a basic syntax with a few forms.
+  + `= 0.2.0` denotes a single vulnerable version.
+  + `<= 1.0.8` denotes a version range up to and including the specified version
+  + `< 0.1.11` denotes a version range up to, but excluding, the specified version
+  + `>= 4.3.0, < 4.3.5` denotes a version range with a known minimum and maximum version.
+  + `>= 0.0.1` denotes a version range with a known minimum, but no known maximum
+
+  """
+  vulnerableVersionRange: String!
+}
+
+"""The connection type for SecurityVulnerability."""
+type SecurityVulnerabilityConnection {
+  """A list of edges."""
+  edges: [SecurityVulnerabilityEdge]
+
+  """A list of nodes."""
+  nodes: [SecurityVulnerability]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type SecurityVulnerabilityEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: SecurityVulnerability
+}
+
+"""Ordering options for security vulnerability connections"""
+input SecurityVulnerabilityOrder {
+  """The field to order security vulnerabilities by."""
+  field: SecurityVulnerabilityOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which security vulnerability connections can be ordered."""
+enum SecurityVulnerabilityOrderField {
+  """Order vulnerability by update time"""
+  UPDATED_AT
+}
+
+"""Represents an S/MIME signature on a Commit or Tag."""
+type SmimeSignature implements GitSignature {
+  """Email used to sign this object."""
+  email: String!
+
+  """True if the signature is valid and verified by GitHub."""
+  isValid: Boolean!
+
+  """
+  Payload for GPG signing object. Raw ODB object without the signature header.
+  """
+  payload: String!
+
+  """ASCII-armored signature header from object."""
+  signature: String!
+
+  """GitHub user corresponding to the email signing this commit."""
+  signer: User
+
+  """
+  The state of this signature. `VALID` if signature is valid and verified by
+  GitHub, otherwise represents reason why signature is considered invalid.
+  """
+  state: GitSignatureState!
+
+  """True if the signature was made with GitHub's signing key."""
+  wasSignedByGitHub: Boolean!
+}
+
+"""The connection type for User."""
+type StargazerConnection {
+  """A list of edges."""
+  edges: [StargazerEdge]
+
+  """A list of nodes."""
+  nodes: [User]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a user that's starred a repository."""
+type StargazerEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+  node: User!
+
+  """Identifies when the item was starred."""
+  starredAt: DateTime!
+}
+
+"""Ways in which star connections can be ordered."""
+input StarOrder {
+  """The field in which to order nodes by."""
+  field: StarOrderField!
+
+  """The direction in which to order nodes."""
+  direction: OrderDirection!
+}
+
+"""Properties by which star connections can be ordered."""
+enum StarOrderField {
+  """Allows ordering a list of stars by when they were created."""
+  STARRED_AT
+}
+
+"""Things that can be starred."""
+interface Starrable {
+  id: ID!
+
+  """A list of users who have starred this starrable."""
+  stargazers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Order for connection"""
+    orderBy: StarOrder
+  ): StargazerConnection!
+
+  """
+  Returns a boolean indicating whether the viewing user has starred this starrable.
+  """
+  viewerHasStarred: Boolean!
+}
+
+"""The connection type for Repository."""
+type StarredRepositoryConnection {
+  """A list of edges."""
+  edges: [StarredRepositoryEdge]
+
+  """A list of nodes."""
+  nodes: [Repository]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a starred repository."""
+type StarredRepositoryEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+  node: Repository!
+
+  """Identifies when the item was starred."""
+  starredAt: DateTime!
+}
+
+"""Represents a commit status."""
+type Status implements Node {
+  """The commit this status is attached to."""
+  commit: Commit
+
+  """Looks up an individual status context by context name."""
+  context(
+    """The context name."""
+    name: String!
+  ): StatusContext
+
+  """The individual status contexts for this commit."""
+  contexts: [StatusContext!]!
+  id: ID!
+
+  """The combined commit status."""
+  state: StatusState!
+}
+
+"""Represents an individual commit status context"""
+type StatusContext implements Node {
+  """This commit this status context is attached to."""
+  commit: Commit
+
+  """The name of this status context."""
+  context: String!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The actor who created this status context."""
+  creator: Actor
+
+  """The description for this status context."""
+  description: String
+  id: ID!
+
+  """The state of this status context."""
+  state: StatusState!
+
+  """The URL for this status context."""
+  targetUrl: URI
+}
+
+"""The possible commit status states."""
+enum StatusState {
+  """Status is expected."""
+  EXPECTED
+
+  """Status is errored."""
+  ERROR
+
+  """Status is failing."""
+  FAILURE
+
+  """Status is pending."""
+  PENDING
+
+  """Status is successful."""
+  SUCCESS
+}
+
+"""Autogenerated input type of SubmitPullRequestReview"""
+input SubmitPullRequestReviewInput {
+  """The Pull Request Review ID to submit."""
+  pullRequestReviewId: ID!
+
+  """The event to send to the Pull Request Review."""
+  event: PullRequestReviewEvent!
+
+  """The text field to set on the Pull Request Review."""
+  body: String
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of SubmitPullRequestReview"""
+type SubmitPullRequestReviewPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The submitted pull request review."""
+  pullRequestReview: PullRequestReview
+}
+
+"""Entities that can be subscribed to for web and email notifications."""
+interface Subscribable {
+  id: ID!
+
+  """
+  Check if the viewer is able to change their subscription status for the repository.
+  """
+  viewerCanSubscribe: Boolean!
+
+  """
+  Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+  """
+  viewerSubscription: SubscriptionState
+}
+
+"""Represents a 'subscribed' event on a given `Subscribable`."""
+type SubscribedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Object referenced by event."""
+  subscribable: Subscribable!
+}
+
+"""The possible states of a subscription."""
+enum SubscriptionState {
+  """The User is only notified when participating or @mentioned."""
+  UNSUBSCRIBED
+
+  """The User is notified of all conversations."""
+  SUBSCRIBED
+
+  """The User is never notified."""
+  IGNORED
+}
+
+"""
+A suggestion to review a pull request based on a user's commit history and review comments.
+"""
+type SuggestedReviewer {
+  """Is this suggestion based on past commits?"""
+  isAuthor: Boolean!
+
+  """Is this suggestion based on past review comments?"""
+  isCommenter: Boolean!
+
+  """Identifies the user suggested to review the pull request."""
+  reviewer: User!
+}
+
+"""Represents a Git tag."""
+type Tag implements Node & GitObject {
+  """An abbreviated version of the Git object ID"""
+  abbreviatedOid: String!
+
+  """The HTTP path for this Git object"""
+  commitResourcePath: URI!
+
+  """The HTTP URL for this Git object"""
+  commitUrl: URI!
+  id: ID!
+
+  """The Git tag message."""
+  message: String
+
+  """The Git tag name."""
+  name: String!
+
+  """The Git object ID"""
+  oid: GitObjectID!
+
+  """The Repository the Git object belongs to"""
+  repository: Repository!
+
+  """Details about the tag author."""
+  tagger: GitActor
+
+  """The Git object the tag points to."""
+  target: GitObject!
+}
+
+"""A team of users in an organization."""
+type Team implements Node & Subscribable & MemberStatusable {
+  """A list of teams that are ancestors of this team."""
+  ancestors(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): TeamConnection!
+
+  """A URL pointing to the team's avatar."""
+  avatarUrl(
+    """The size in pixels of the resulting square image."""
+    size: Int = 400
+  ): URI
+
+  """List of child teams belonging to this team"""
+  childTeams(
+    """Order for connection"""
+    orderBy: TeamOrder
+
+    """User logins to filter by"""
+    userLogins: [String!]
+
+    """Whether to list immediate child teams or all descendant child teams."""
+    immediateOnly: Boolean = true
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): TeamConnection!
+
+  """The slug corresponding to the organization and team."""
+  combinedSlug: String!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The description of the team."""
+  description: String
+
+  """The HTTP path for editing this team"""
+  editTeamResourcePath: URI!
+
+  """The HTTP URL for editing this team"""
+  editTeamUrl: URI!
+  id: ID!
+
+  """A list of pending invitations for users to this team"""
+  invitations(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): OrganizationInvitationConnection
+
+  """
+  Get the status messages members of this entity have set that are either public or visible only to the organization.
+  """
+  memberStatuses(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Ordering options for user statuses returned from the connection."""
+    orderBy: UserStatusOrder
+  ): UserStatusConnection!
+
+  """A list of users who are members of this team."""
+  members(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """The search string to look for."""
+    query: String
+
+    """Filter by membership type"""
+    membership: TeamMembershipType = ALL
+
+    """Filter by team member role"""
+    role: TeamMemberRole
+
+    """Order for the connection."""
+    orderBy: TeamMemberOrder
+  ): TeamMemberConnection!
+
+  """The HTTP path for the team' members"""
+  membersResourcePath: URI!
+
+  """The HTTP URL for the team' members"""
+  membersUrl: URI!
+
+  """The name of the team."""
+  name: String!
+
+  """The HTTP path creating a new team"""
+  newTeamResourcePath: URI!
+
+  """The HTTP URL creating a new team"""
+  newTeamUrl: URI!
+
+  """The organization that owns this team."""
+  organization: Organization!
+
+  """The parent team of the team."""
+  parentTeam: Team
+
+  """The level of privacy the team has."""
+  privacy: TeamPrivacy!
+
+  """A list of repositories this team has access to."""
+  repositories(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """The search string to look for."""
+    query: String
+
+    """Order for the connection."""
+    orderBy: TeamRepositoryOrder
+  ): TeamRepositoryConnection!
+
+  """The HTTP path for this team's repositories"""
+  repositoriesResourcePath: URI!
+
+  """The HTTP URL for this team's repositories"""
+  repositoriesUrl: URI!
+
+  """The HTTP path for this team"""
+  resourcePath: URI!
+
+  """The slug corresponding to the team."""
+  slug: String!
+
+  """The HTTP path for this team's teams"""
+  teamsResourcePath: URI!
+
+  """The HTTP URL for this team's teams"""
+  teamsUrl: URI!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this team"""
+  url: URI!
+
+  """Team is adminable by the viewer."""
+  viewerCanAdminister: Boolean!
+
+  """
+  Check if the viewer is able to change their subscription status for the repository.
+  """
+  viewerCanSubscribe: Boolean!
+
+  """
+  Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.
+  """
+  viewerSubscription: SubscriptionState
+}
+
+"""The connection type for Team."""
+type TeamConnection {
+  """A list of edges."""
+  edges: [TeamEdge]
+
+  """A list of nodes."""
+  nodes: [Team]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type TeamEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Team
+}
+
+"""The connection type for User."""
+type TeamMemberConnection {
+  """A list of edges."""
+  edges: [TeamMemberEdge]
+
+  """A list of nodes."""
+  nodes: [User]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a user who is a member of a team."""
+type TeamMemberEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The HTTP path to the organization's member access page."""
+  memberAccessResourcePath: URI!
+
+  """The HTTP URL to the organization's member access page."""
+  memberAccessUrl: URI!
+  node: User!
+
+  """The role the member has on the team."""
+  role: TeamMemberRole!
+}
+
+"""Ordering options for team member connections"""
+input TeamMemberOrder {
+  """The field to order team members by."""
+  field: TeamMemberOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which team member connections can be ordered."""
+enum TeamMemberOrderField {
+  """Order team members by login"""
+  LOGIN
+
+  """Order team members by creation time"""
+  CREATED_AT
+}
+
+"""The possible team member roles; either 'maintainer' or 'member'."""
+enum TeamMemberRole {
+  """A team maintainer has permission to add and remove team members."""
+  MAINTAINER
+
+  """A team member has no administrative permissions on the team."""
+  MEMBER
+}
+
+"""
+Defines which types of team members are included in the returned list. Can be one of IMMEDIATE, CHILD_TEAM or ALL.
+"""
+enum TeamMembershipType {
+  """Includes only immediate members of the team."""
+  IMMEDIATE
+
+  """Includes only child team members for the team."""
+  CHILD_TEAM
+
+  """Includes immediate and child team members for the team."""
+  ALL
+}
+
+"""Ways in which team connections can be ordered."""
+input TeamOrder {
+  """The field in which to order nodes by."""
+  field: TeamOrderField!
+
+  """The direction in which to order nodes."""
+  direction: OrderDirection!
+}
+
+"""Properties by which team connections can be ordered."""
+enum TeamOrderField {
+  """Allows ordering a list of teams by name."""
+  NAME
+}
+
+"""The possible team privacy values."""
+enum TeamPrivacy {
+  """A secret team can only be seen by its members."""
+  SECRET
+
+  """
+  A visible team can be seen and @mentioned by every member of the organization.
+  """
+  VISIBLE
+}
+
+"""The connection type for Repository."""
+type TeamRepositoryConnection {
+  """A list of edges."""
+  edges: [TeamRepositoryEdge]
+
+  """A list of nodes."""
+  nodes: [Repository]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""Represents a team repository."""
+type TeamRepositoryEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+  node: Repository!
+
+  """The permission level the team has on the repository"""
+  permission: RepositoryPermission!
+}
+
+"""Ordering options for team repository connections"""
+input TeamRepositoryOrder {
+  """The field to order repositories by."""
+  field: TeamRepositoryOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which team repository connections can be ordered."""
+enum TeamRepositoryOrderField {
+  """Order repositories by creation time"""
+  CREATED_AT
+
+  """Order repositories by update time"""
+  UPDATED_AT
+
+  """Order repositories by push time"""
+  PUSHED_AT
+
+  """Order repositories by name"""
+  NAME
+
+  """Order repositories by permission"""
+  PERMISSION
+
+  """Order repositories by number of stargazers"""
+  STARGAZERS
+}
+
+"""The role of a user on a team."""
+enum TeamRole {
+  """User has admin rights on the team."""
+  ADMIN
+
+  """User is a member of the team."""
+  MEMBER
+}
+
+"""A text match within a search result."""
+type TextMatch {
+  """The specific text fragment within the property matched on."""
+  fragment: String!
+
+  """Highlights within the matched fragment."""
+  highlights: [TextMatchHighlight!]!
+
+  """The property matched on."""
+  property: String!
+}
+
+"""Represents a single highlight in a search result match."""
+type TextMatchHighlight {
+  """The indice in the fragment where the matched text begins."""
+  beginIndice: Int!
+
+  """The indice in the fragment where the matched text ends."""
+  endIndice: Int!
+
+  """The text matched."""
+  text: String!
+}
+
+"""A topic aggregates entities that are related to a subject."""
+type Topic implements Node & Starrable {
+  id: ID!
+
+  """The topic's name."""
+  name: String!
+
+  """
+  A list of related topics, including aliases of this topic, sorted with the most relevant
+  first. Returns up to 10 Topics.
+
+  """
+  relatedTopics(
+    """How many topics to return."""
+    first: Int = 3
+  ): [Topic!]!
+
+  """A list of users who have starred this starrable."""
+  stargazers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """Order for connection"""
+    orderBy: StarOrder
+  ): StargazerConnection!
+
+  """
+  Returns a boolean indicating whether the viewing user has starred this starrable.
+  """
+  viewerHasStarred: Boolean!
+}
+
+"""The connection type for Topic."""
+type TopicConnection {
+  """A list of edges."""
+  edges: [TopicEdge]
+
+  """A list of nodes."""
+  nodes: [Topic]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type TopicEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: Topic
+}
+
+"""Reason that the suggested topic is declined."""
+enum TopicSuggestionDeclineReason {
+  """The suggested topic is not relevant to the repository."""
+  NOT_RELEVANT
+
+  """
+  The suggested topic is too specific for the repository (e.g. #ruby-on-rails-version-4-2-1).
+  """
+  TOO_SPECIFIC
+
+  """The viewer does not like the suggested topic."""
+  PERSONAL_PREFERENCE
+
+  """The suggested topic is too general for the repository."""
+  TOO_GENERAL
+}
+
+"""Represents a 'transferred' event on a given issue or pull request."""
+type TransferredEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """The repository this came from"""
+  fromRepository: Repository
+  id: ID!
+
+  """Identifies the issue associated with the event."""
+  issue: Issue!
+}
+
+"""Represents a Git tree."""
+type Tree implements Node & GitObject {
+  """An abbreviated version of the Git object ID"""
+  abbreviatedOid: String!
+
+  """The HTTP path for this Git object"""
+  commitResourcePath: URI!
+
+  """The HTTP URL for this Git object"""
+  commitUrl: URI!
+
+  """A list of tree entries."""
+  entries: [TreeEntry!]
+  id: ID!
+
+  """The Git object ID"""
+  oid: GitObjectID!
+
+  """The Repository the Git object belongs to"""
+  repository: Repository!
+}
+
+"""Represents a Git tree entry."""
+type TreeEntry {
+  """Entry file mode."""
+  mode: Int!
+
+  """Entry file name."""
+  name: String!
+
+  """Entry file object."""
+  object: GitObject
+
+  """Entry file Git object ID."""
+  oid: GitObjectID!
+
+  """The Repository the tree entry belongs to"""
+  repository: Repository!
+
+  """Entry file type."""
+  type: String!
+}
+
+"""Represents an 'unassigned' event on any assignable object."""
+type UnassignedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the assignable associated with the event."""
+  assignable: Assignable!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Identifies the subject (user) who was unassigned."""
+  user: User
+}
+
+"""Represents a type that can be retrieved by a URL."""
+interface UniformResourceLocatable {
+  """The HTML path to this resource."""
+  resourcePath: URI!
+
+  """The URL to this resource."""
+  url: URI!
+}
+
+"""Represents an unknown signature on a Commit or Tag."""
+type UnknownSignature implements GitSignature {
+  """Email used to sign this object."""
+  email: String!
+
+  """True if the signature is valid and verified by GitHub."""
+  isValid: Boolean!
+
+  """
+  Payload for GPG signing object. Raw ODB object without the signature header.
+  """
+  payload: String!
+
+  """ASCII-armored signature header from object."""
+  signature: String!
+
+  """GitHub user corresponding to the email signing this commit."""
+  signer: User
+
+  """
+  The state of this signature. `VALID` if signature is valid and verified by
+  GitHub, otherwise represents reason why signature is considered invalid.
+  """
+  state: GitSignatureState!
+
+  """True if the signature was made with GitHub's signing key."""
+  wasSignedByGitHub: Boolean!
+}
+
+"""Represents an 'unlabeled' event on a given issue or pull request."""
+type UnlabeledEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Identifies the label associated with the 'unlabeled' event."""
+  label: Label!
+
+  """Identifies the `Labelable` associated with the event."""
+  labelable: Labelable!
+}
+
+"""Represents an 'unlocked' event on a given issue or pull request."""
+type UnlockedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Object that was unlocked."""
+  lockable: Lockable!
+}
+
+"""Autogenerated input type of UnlockLockable"""
+input UnlockLockableInput {
+  """ID of the issue or pull request to be unlocked."""
+  lockableId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UnlockLockable"""
+type UnlockLockablePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The item that was unlocked."""
+  unlockedRecord: Lockable
+}
+
+"""Autogenerated input type of UnmarkIssueAsDuplicate"""
+input UnmarkIssueAsDuplicateInput {
+  """ID of the issue or pull request currently marked as a duplicate."""
+  duplicateId: ID!
+
+  """
+  ID of the issue or pull request currently considered canonical/authoritative/original.
+  """
+  canonicalId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UnmarkIssueAsDuplicate"""
+type UnmarkIssueAsDuplicatePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The issue or pull request that was marked as a duplicate."""
+  duplicate: IssueOrPullRequest
+}
+
+"""Autogenerated input type of UnminimizeComment"""
+input UnminimizeCommentInput {
+  """The Node ID of the subject to modify."""
+  subjectId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated input type of UnpinIssue"""
+input UnpinIssueInput {
+  """The ID of the issue to be unpinned"""
+  issueId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Represents an 'unpinned' event on a given issue or pull request."""
+type UnpinnedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Identifies the issue associated with the event."""
+  issue: Issue!
+}
+
+"""Autogenerated input type of UnresolveReviewThread"""
+input UnresolveReviewThreadInput {
+  """The ID of the thread to unresolve"""
+  threadId: ID!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UnresolveReviewThread"""
+type UnresolveReviewThreadPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The thread to resolve."""
+  thread: PullRequestReviewThread
+}
+
+"""Represents an 'unsubscribed' event on a given `Subscribable`."""
+type UnsubscribedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """Object referenced by event."""
+  subscribable: Subscribable!
+}
+
+"""Entities that can be updated."""
+interface Updatable {
+  """Check if the current viewer can update this object."""
+  viewerCanUpdate: Boolean!
+}
+
+"""Comments that can be updated."""
+interface UpdatableComment {
+  """Reasons why the current viewer can not update this comment."""
+  viewerCannotUpdateReasons: [CommentCannotUpdateReason!]!
+}
+
+"""Autogenerated input type of UpdateBranchProtectionRule"""
+input UpdateBranchProtectionRuleInput {
+  """The global relay id of the branch protection rule to be updated."""
+  branchProtectionRuleId: ID!
+
+  """The glob-like pattern used to determine matching branches."""
+  pattern: String
+
+  """Are approving reviews required to update matching branches."""
+  requiresApprovingReviews: Boolean
+
+  """Number of approving reviews required to update matching branches."""
+  requiredApprovingReviewCount: Int
+
+  """Are commits required to be signed."""
+  requiresCommitSignatures: Boolean
+
+  """Can admins overwrite branch protection."""
+  isAdminEnforced: Boolean
+
+  """Are status checks required to update matching branches."""
+  requiresStatusChecks: Boolean
+
+  """Are branches required to be up to date before merging."""
+  requiresStrictStatusChecks: Boolean
+
+  """Are reviews from code owners required to update matching branches."""
+  requiresCodeOwnerReviews: Boolean
+
+  """
+  Will new commits pushed to matching branches dismiss pull request review approvals.
+  """
+  dismissesStaleReviews: Boolean
+
+  """Is dismissal of pull request reviews restricted."""
+  restrictsReviewDismissals: Boolean
+
+  """
+  A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.
+  """
+  reviewDismissalActorIds: [ID!]
+
+  """Is pushing to matching branches restricted."""
+  restrictsPushes: Boolean
+
+  """A list of User or Team IDs allowed to push to matching branches."""
+  pushActorIds: [ID!]
+
+  """
+  List of required status check contexts that must pass for commits to be accepted to matching branches.
+  """
+  requiredStatusCheckContexts: [String!]
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdateBranchProtectionRule"""
+type UpdateBranchProtectionRulePayload {
+  """The newly created BranchProtectionRule."""
+  branchProtectionRule: BranchProtectionRule
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated input type of UpdateIssueComment"""
+input UpdateIssueCommentInput {
+  """The ID of the IssueComment to modify."""
+  id: ID!
+
+  """The updated text of the comment."""
+  body: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdateIssueComment"""
+type UpdateIssueCommentPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The updated comment."""
+  issueComment: IssueComment
+}
+
+"""Autogenerated input type of UpdateIssue"""
+input UpdateIssueInput {
+  """The ID of the Issue to modify."""
+  id: ID!
+
+  """The title for the issue."""
+  title: String
+
+  """The body for the issue description."""
+  body: String
+
+  """An array of Node IDs of users for this issue."""
+  assigneeIds: [ID!]
+
+  """The Node ID of the milestone for this issue."""
+  milestoneId: ID
+
+  """An array of Node IDs of labels for this issue."""
+  labelIds: [ID!]
+
+  """The desired issue state."""
+  state: IssueState
+
+  """An array of Node IDs for projects associated with this issue."""
+  projectIds: [ID!]
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdateIssue"""
+type UpdateIssuePayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The issue."""
+  issue: Issue
+}
+
+"""Autogenerated input type of UpdateProjectCard"""
+input UpdateProjectCardInput {
+  """The ProjectCard ID to update."""
+  projectCardId: ID!
+
+  """Whether or not the ProjectCard should be archived"""
+  isArchived: Boolean
+
+  """The note of ProjectCard."""
+  note: String
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdateProjectCard"""
+type UpdateProjectCardPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The updated ProjectCard."""
+  projectCard: ProjectCard
+}
+
+"""Autogenerated input type of UpdateProjectColumn"""
+input UpdateProjectColumnInput {
+  """The ProjectColumn ID to update."""
+  projectColumnId: ID!
+
+  """The name of project column."""
+  name: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdateProjectColumn"""
+type UpdateProjectColumnPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The updated project column."""
+  projectColumn: ProjectColumn
+}
+
+"""Autogenerated input type of UpdateProject"""
+input UpdateProjectInput {
+  """The Project ID to update."""
+  projectId: ID!
+
+  """The name of project."""
+  name: String
+
+  """The description of project."""
+  body: String
+
+  """Whether the project is open or closed."""
+  state: ProjectState
+
+  """Whether the project is public or not."""
+  public: Boolean
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdateProject"""
+type UpdateProjectPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The updated project."""
+  project: Project
+}
+
+"""Autogenerated input type of UpdatePullRequest"""
+input UpdatePullRequestInput {
+  """The Node ID of the pull request."""
+  pullRequestId: ID!
+
+  """
+  The name of the branch you want your changes pulled into. This should be an existing branch
+  on the current repository.
+
+  """
+  baseRefName: String
+
+  """The title of the pull request."""
+  title: String
+
+  """The contents of the pull request."""
+  body: String
+
+  """Indicates whether maintainers can modify the pull request."""
+  maintainerCanModify: Boolean
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdatePullRequest"""
+type UpdatePullRequestPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The updated pull request."""
+  pullRequest: PullRequest
+}
+
+"""Autogenerated input type of UpdatePullRequestReviewComment"""
+input UpdatePullRequestReviewCommentInput {
+  """The Node ID of the comment to modify."""
+  pullRequestReviewCommentId: ID!
+
+  """The text of the comment."""
+  body: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdatePullRequestReviewComment"""
+type UpdatePullRequestReviewCommentPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The updated comment."""
+  pullRequestReviewComment: PullRequestReviewComment
+}
+
+"""Autogenerated input type of UpdatePullRequestReview"""
+input UpdatePullRequestReviewInput {
+  """The Node ID of the pull request review to modify."""
+  pullRequestReviewId: ID!
+
+  """The contents of the pull request review body."""
+  body: String!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdatePullRequestReview"""
+type UpdatePullRequestReviewPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The updated pull request review."""
+  pullRequestReview: PullRequestReview
+}
+
+"""Autogenerated input type of UpdateSubscription"""
+input UpdateSubscriptionInput {
+  """The Node ID of the subscribable object to modify."""
+  subscribableId: ID!
+
+  """The new state of the subscription."""
+  state: SubscriptionState!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdateSubscription"""
+type UpdateSubscriptionPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """The input subscribable entity."""
+  subscribable: Subscribable
+}
+
+"""Autogenerated input type of UpdateTopics"""
+input UpdateTopicsInput {
+  """The Node ID of the repository."""
+  repositoryId: ID!
+
+  """An array of topic names."""
+  topicNames: [String!]!
+
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+}
+
+"""Autogenerated return type of UpdateTopics"""
+type UpdateTopicsPayload {
+  """A unique identifier for the client performing the mutation."""
+  clientMutationId: String
+
+  """Names of the provided topics that are not valid."""
+  invalidTopicNames: [String!]
+
+  """The updated repository."""
+  repository: Repository
+}
+
+"""An RFC 3986, RFC 3987, and RFC 6570 (level 4) compliant URI string."""
+scalar URI
+
+"""
+A user is an individual's account on GitHub that owns repositories and can make new content.
+"""
+type User implements Node & Actor & RegistryPackageOwner & RegistryPackageSearch & ProjectOwner & RepositoryOwner & UniformResourceLocatable & ProfileOwner {
+  """
+  Determine if this repository owner has any items that can be pinned to their profile.
+  """
+  anyPinnableItems(
+    """Filter to only a particular kind of pinnable item."""
+    type: PinnableItemType
+  ): Boolean!
+
+  """A URL pointing to the user's public avatar."""
+  avatarUrl(
+    """The size of the resulting square image."""
+    size: Int
+  ): URI!
+
+  """The user's public profile bio."""
+  bio: String
+
+  """The user's public profile bio as HTML."""
+  bioHTML: HTML!
+
+  """A list of commit comments made by this user."""
+  commitComments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): CommitCommentConnection!
+
+  """The user's public profile company."""
+  company: String
+
+  """The user's public profile company as HTML."""
+  companyHTML: HTML!
+
+  """
+  The collection of contributions this user has made to different repositories.
+  """
+  contributionsCollection(
+    """The ID of the organization used to filter contributions."""
+    organizationID: ID
+
+    """
+    Only contributions made at this time or later will be counted. If omitted, defaults to a year ago.
+    """
+    from: DateTime
+
+    """
+    Only contributions made before and up to and including this time will be
+    counted. If omitted, defaults to the current time.
+    """
+    to: DateTime
+  ): ContributionsCollection!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the primary key from the database."""
+  databaseId: Int
+
+  """The user's publicly visible profile email."""
+  email: String!
+
+  """A list of users the given user is followed by."""
+  followers(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): FollowerConnection!
+
+  """A list of users the given user is following."""
+  following(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): FollowingConnection!
+
+  """Find gist by repo name."""
+  gist(
+    """The gist name to find."""
+    name: String!
+  ): Gist
+
+  """A list of gist comments made by this user."""
+  gistComments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): GistCommentConnection!
+
+  """A list of the Gists the user has created."""
+  gists(
+    """Filters Gists according to privacy."""
+    privacy: GistPrivacy
+
+    """Ordering options for gists returned from the connection"""
+    orderBy: GistOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): GistConnection!
+  id: ID!
+
+  """
+  Whether or not this user is a participant in the GitHub Security Bug Bounty.
+  """
+  isBountyHunter: Boolean!
+
+  """
+  Whether or not this user is a participant in the GitHub Campus Experts Program.
+  """
+  isCampusExpert: Boolean!
+
+  """Whether or not this user is a GitHub Developer Program member."""
+  isDeveloperProgramMember: Boolean!
+
+  """Whether or not this user is a GitHub employee."""
+  isEmployee: Boolean!
+
+  """Whether or not the user has marked themselves as for hire."""
+  isHireable: Boolean!
+
+  """Whether or not this user is a site administrator."""
+  isSiteAdmin: Boolean!
+
+  """Whether or not this user is the viewing user."""
+  isViewer: Boolean!
+
+  """A list of issue comments made by this user."""
+  issueComments(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueCommentConnection!
+
+  """A list of issues associated with this user."""
+  issues(
+    """Ordering options for issues returned from the connection."""
+    orderBy: IssueOrder
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """A list of states to filter the issues by."""
+    states: [IssueState!]
+
+    """Filtering options for issues returned from the connection."""
+    filterBy: IssueFilters
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): IssueConnection!
+
+  """
+  Showcases a selection of repositories and gists that the profile owner has
+  either curated or that have been selected automatically based on popularity.
+  """
+  itemShowcase: ProfileItemShowcase!
+
+  """The user's public profile location."""
+  location: String
+
+  """The username used to login."""
+  login: String!
+
+  """The user's public profile name."""
+  name: String
+
+  """Find an organization by its login that the user belongs to."""
+  organization(
+    """The login of the organization to find."""
+    login: String!
+  ): Organization
+
+  """A list of organizations the user belongs to."""
+  organizations(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): OrganizationConnection!
+
+  """
+  A list of repositories and gists this profile owner can pin to their profile.
+  """
+  pinnableItems(
+    """Filter the types of pinnable items that are returned."""
+    types: [PinnableItemType!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PinnableItemConnection!
+
+  """
+  A list of repositories and gists this profile owner has pinned to their profile
+  """
+  pinnedItems(
+    """Filter the types of pinned items that are returned."""
+    types: [PinnableItemType!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PinnableItemConnection!
+
+  """
+  Returns how many more items this profile owner can pin to their profile.
+  """
+  pinnedItemsRemaining: Int!
+
+  """A list of repositories this user has pinned to their profile"""
+  pinnedRepositories(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """
+    Array of viewer's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    current viewer owns.
+    """
+    affiliations: [RepositoryAffiliation]
+
+    """
+    Array of owner's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    organization or user being viewed owns.
+    """
+    ownerAffiliations: [RepositoryAffiliation]
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RepositoryConnection! @deprecated(reason: "pinnedRepositories will be removed Use ProfileOwner.pinnedItems instead. Removal on 2019-07-01 UTC.")
+
+  """Find project by number."""
+  project(
+    """The project number to find."""
+    number: Int!
+  ): Project
+
+  """A list of projects under the owner."""
+  projects(
+    """Ordering options for projects returned from the connection"""
+    orderBy: ProjectOrder
+
+    """Query to search projects by, currently only searching by name."""
+    search: String
+
+    """A list of states to filter the projects by."""
+    states: [ProjectState!]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): ProjectConnection!
+
+  """The HTTP path listing user's projects"""
+  projectsResourcePath: URI!
+
+  """The HTTP URL listing user's projects"""
+  projectsUrl: URI!
+
+  """A list of public keys associated with this user."""
+  publicKeys(
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PublicKeyConnection!
+
+  """A list of pull requests associated with this user."""
+  pullRequests(
+    """A list of states to filter the pull requests by."""
+    states: [PullRequestState!]
+
+    """A list of label names to filter the pull requests by."""
+    labels: [String!]
+
+    """The head ref name to filter the pull requests by."""
+    headRefName: String
+
+    """The base ref name to filter the pull requests by."""
+    baseRefName: String
+
+    """Ordering options for pull requests returned from the connection."""
+    orderBy: IssueOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): PullRequestConnection!
+
+  """A list of repositories that the user owns."""
+  repositories(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """
+    Array of viewer's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    current viewer owns.
+    """
+    affiliations: [RepositoryAffiliation]
+
+    """
+    Array of owner's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    organization or user being viewed owns.
+    """
+    ownerAffiliations: [RepositoryAffiliation]
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+
+    """
+    If non-null, filters repositories according to whether they are forks of another repository
+    """
+    isFork: Boolean
+  ): RepositoryConnection!
+
+  """A list of repositories that the user recently contributed to."""
+  repositoriesContributedTo(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """If true, include user repositories"""
+    includeUserRepositories: Boolean
+
+    """
+    If non-null, include only the specified types of contributions. The
+    GitHub.com UI uses [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]
+    """
+    contributionTypes: [RepositoryContributionType]
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RepositoryConnection!
+
+  """Find Repository."""
+  repository(
+    """Name of Repository to find."""
+    name: String!
+  ): Repository
+
+  """The HTTP path for this user"""
+  resourcePath: URI!
+
+  """Repositories the user has starred."""
+  starredRepositories(
+    """
+    Filters starred repositories to only return repositories owned by the viewer.
+    """
+    ownedByViewer: Boolean
+
+    """Order for connection"""
+    orderBy: StarOrder
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): StarredRepositoryConnection!
+
+  """The user's description of what they're currently doing."""
+  status: UserStatus
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The HTTP URL for this user"""
+  url: URI!
+
+  """Can the viewer pin repositories and gists to the profile?"""
+  viewerCanChangePinnedItems: Boolean!
+
+  """Can the current viewer create new projects on this owner."""
+  viewerCanCreateProjects: Boolean!
+
+  """Whether or not the viewer is able to follow the user."""
+  viewerCanFollow: Boolean!
+
+  """Whether or not this user is followed by the viewer."""
+  viewerIsFollowing: Boolean!
+
+  """A list of repositories the given user is watching."""
+  watching(
+    """If non-null, filters repositories according to privacy"""
+    privacy: RepositoryPrivacy
+
+    """Ordering options for repositories returned from the connection"""
+    orderBy: RepositoryOrder
+
+    """Affiliation options for repositories returned from the connection"""
+    affiliations: [RepositoryAffiliation]
+
+    """
+    Array of owner's affiliation options for repositories returned from the
+    connection. For example, OWNER will include only repositories that the
+    organization or user being viewed owns.
+    """
+    ownerAffiliations: [RepositoryAffiliation]
+
+    """
+    If non-null, filters repositories according to whether they have been locked
+    """
+    isLocked: Boolean
+
+    """Returns the elements in the list that come after the specified cursor."""
+    after: String
+
+    """
+    Returns the elements in the list that come before the specified cursor.
+    """
+    before: String
+
+    """Returns the first _n_ elements from the list."""
+    first: Int
+
+    """Returns the last _n_ elements from the list."""
+    last: Int
+  ): RepositoryConnection!
+
+  """A URL pointing to the user's public website/blog."""
+  websiteUrl: URI
+}
+
+"""The possible durations that a user can be blocked for."""
+enum UserBlockDuration {
+  """The user was blocked for 1 day"""
+  ONE_DAY
+
+  """The user was blocked for 3 days"""
+  THREE_DAYS
+
+  """The user was blocked for 7 days"""
+  ONE_WEEK
+
+  """The user was blocked for 30 days"""
+  ONE_MONTH
+
+  """The user was blocked permanently"""
+  PERMANENT
+}
+
+"""Represents a 'user_blocked' event on a given user."""
+type UserBlockedEvent implements Node {
+  """Identifies the actor who performed the event."""
+  actor: Actor
+
+  """Number of days that the user was blocked for."""
+  blockDuration: UserBlockDuration!
+
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+  id: ID!
+
+  """The user who was blocked."""
+  subject: User
+}
+
+"""The connection type for User."""
+type UserConnection {
+  """A list of edges."""
+  edges: [UserEdge]
+
+  """A list of nodes."""
+  nodes: [User]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edit on user content"""
+type UserContentEdit implements Node {
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """Identifies the date and time when the object was deleted."""
+  deletedAt: DateTime
+
+  """The actor who deleted this content"""
+  deletedBy: Actor
+
+  """A summary of the changes for this edit"""
+  diff: String
+
+  """When this content was edited"""
+  editedAt: DateTime!
+
+  """The actor who edited this content"""
+  editor: Actor
+  id: ID!
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+}
+
+"""A list of edits to content."""
+type UserContentEditConnection {
+  """A list of edges."""
+  edges: [UserContentEditEdge]
+
+  """A list of nodes."""
+  nodes: [UserContentEdit]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type UserContentEditEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: UserContentEdit
+}
+
+"""Represents a user."""
+type UserEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: User
+}
+
+"""The user's description of what they're currently doing."""
+type UserStatus implements Node {
+  """Identifies the date and time when the object was created."""
+  createdAt: DateTime!
+
+  """An emoji summarizing the user's status."""
+  emoji: String
+
+  """ID of the object."""
+  id: ID!
+
+  """
+  Whether this status indicates the user is not fully available on GitHub.
+  """
+  indicatesLimitedAvailability: Boolean!
+
+  """A brief message describing what the user is doing."""
+  message: String
+
+  """
+  The organization whose members can see this status. If null, this status is publicly visible.
+  """
+  organization: Organization
+
+  """Identifies the date and time when the object was last updated."""
+  updatedAt: DateTime!
+
+  """The user who has this status."""
+  user: User!
+}
+
+"""The connection type for UserStatus."""
+type UserStatusConnection {
+  """A list of edges."""
+  edges: [UserStatusEdge]
+
+  """A list of nodes."""
+  nodes: [UserStatus]
+
+  """Information to aid in pagination."""
+  pageInfo: PageInfo!
+
+  """Identifies the total count of items in the connection."""
+  totalCount: Int!
+}
+
+"""An edge in a connection."""
+type UserStatusEdge {
+  """A cursor for use in pagination."""
+  cursor: String!
+
+  """The item at the end of the edge."""
+  node: UserStatus
+}
+
+"""Ordering options for user status connections."""
+input UserStatusOrder {
+  """The field to order user statuses by."""
+  field: UserStatusOrderField!
+
+  """The ordering direction."""
+  direction: OrderDirection!
+}
+
+"""Properties by which user status connections can be ordered."""
+enum UserStatusOrderField {
+  """Order user statuses by when they were updated."""
+  UPDATED_AT
+}
+
+"""A valid x509 certificate string"""
+scalar X509Certificate
diff --git a/tests/fixtures/github_schema.json b/tests/fixtures/github_schema.json
new file mode 100644
index 0000000..7352a87
--- /dev/null
+++ b/tests/fixtures/github_schema.json
@@ -0,0 +1,56836 @@
+{
+  "data": {
+    "__schema": {
+      "queryType": {
+        "name": "Query"
+      },
+      "mutationType": {
+        "name": "Mutation"
+      },
+      "subscriptionType": null,
+      "types": [
+        {
+          "kind": "SCALAR",
+          "name": "Boolean",
+          "description": "Represents `true` or `false` values.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "SCALAR",
+          "name": "String",
+          "description": "Represents textual data as UTF-8 character sequences. This type is most often used by GraphQL to represent free-form human-readable text.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Query",
+          "description": "The query root of GitHub's GraphQL interface.",
+          "fields": [
+            {
+              "name": "codeOfConduct",
+              "description": "Look up a code of conduct by its key",
+              "args": [
+                {
+                  "name": "key",
+                  "description": "The code of conduct's key",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CodeOfConduct",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "codesOfConduct",
+              "description": "Look up a code of conduct by its key",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CodeOfConduct",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "license",
+              "description": "Look up an open source license by its key",
+              "args": [
+                {
+                  "name": "key",
+                  "description": "The license's downcased SPDX ID",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "License",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "licenses",
+              "description": "Return a list of known open source licenses",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "License",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "marketplaceCategories",
+              "description": "Get alphabetically sorted list of Marketplace categories",
+              "args": [
+                {
+                  "name": "includeCategories",
+                  "description": "Return only the specified categories.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "excludeEmpty",
+                  "description": "Exclude categories with no listings.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "excludeSubcategories",
+                  "description": "Returns top level categories only, excluding any subcategories.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "MarketplaceCategory",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "marketplaceCategory",
+              "description": "Look up a Marketplace category by its slug.",
+              "args": [
+                {
+                  "name": "slug",
+                  "description": "The URL slug of the category.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "useTopicAliases",
+                  "description": "Also check topic aliases for the category slug",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "MarketplaceCategory",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "marketplaceListing",
+              "description": "Look up a single Marketplace listing",
+              "args": [
+                {
+                  "name": "slug",
+                  "description": "Select the listing that matches this slug. It's the short name of the listing used in its URL.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "MarketplaceListing",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "marketplaceListings",
+              "description": "Look up Marketplace listings",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "categorySlug",
+                  "description": "Select only listings with the given category.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "useTopicAliases",
+                  "description": "Also check topic aliases for the category slug",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "viewerCanAdmin",
+                  "description": "Select listings to which user has admin access. If omitted, listings visible to the\nviewer are returned.\n",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "adminId",
+                  "description": "Select listings that can be administered by the specified user.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "organizationId",
+                  "description": "Select listings for products owned by the specified organization.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "allStates",
+                  "description": "Select listings visible to the viewer even if they are not approved. If omitted or\nfalse, only approved listings will be returned.\n",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "slugs",
+                  "description": "Select the listings with these slugs, if they are visible to the viewer.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "primaryCategoryOnly",
+                  "description": "Select only listings where the primary category matches the given category slug.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "withFreeTrialsOnly",
+                  "description": "Select only listings that offer a free trial.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "MarketplaceListingConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "meta",
+              "description": "Return information about the GitHub instance",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "GitHubMetadata",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "Fetches an object given its ID.",
+              "args": [
+                {
+                  "name": "id",
+                  "description": "ID of the object.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "ID",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Node",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "Lookup nodes by a list of IDs.",
+              "args": [
+                {
+                  "name": "ids",
+                  "description": "The list of node IDs.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "LIST",
+                      "name": null,
+                      "ofType": {
+                        "kind": "NON_NULL",
+                        "name": null,
+                        "ofType": {
+                          "kind": "SCALAR",
+                          "name": "ID",
+                          "ofType": null
+                        }
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "INTERFACE",
+                    "name": "Node",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organization",
+              "description": "Lookup a organization by login.",
+              "args": [
+                {
+                  "name": "login",
+                  "description": "The organization's login.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Organization",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "rateLimit",
+              "description": "The client's rate limit information.",
+              "args": [
+                {
+                  "name": "dryRun",
+                  "description": "If true, calculate the cost for the query without evaluating it",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RateLimit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "relay",
+              "description": "Hack to workaround https://github.com/facebook/relay/issues/112 re-exposing the root query object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Query",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "Lookup a given repository by the owner and repository name.",
+              "args": [
+                {
+                  "name": "owner",
+                  "description": "The login field of a user or organization",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "name",
+                  "description": "The name of the repository",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositoryOwner",
+              "description": "Lookup a repository owner (ie. either a User or an Organization) by login.",
+              "args": [
+                {
+                  "name": "login",
+                  "description": "The username to lookup the owner by.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "RepositoryOwner",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resource",
+              "description": "Lookup resource by a URL.",
+              "args": [
+                {
+                  "name": "url",
+                  "description": "The URL.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "URI",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "UniformResourceLocatable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "search",
+              "description": "Perform a search across resources.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "query",
+                  "description": "The search string to look for.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "type",
+                  "description": "The types of search items to search within.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "SearchType",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SearchResultItemConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "securityAdvisories",
+              "description": "GitHub Security Advisories",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for the returned topics.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "SecurityAdvisoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"UPDATED_AT\",direction:\"DESC\"}"
+                },
+                {
+                  "name": "identifier",
+                  "description": "Filter advisories by identifier, e.g. GHSA or CVE.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "SecurityAdvisoryIdentifierFilter",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "publishedSince",
+                  "description": "Filter advisories to those published since a time in the past.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "DateTime",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "updatedSince",
+                  "description": "Filter advisories to those updated since a time in the past.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "DateTime",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityAdvisoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "securityAdvisory",
+              "description": "Fetch a Security Advisory by its GHSA ID",
+              "args": [
+                {
+                  "name": "ghsaId",
+                  "description": "GitHub Security Advisory ID.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "SecurityAdvisory",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "securityVulnerabilities",
+              "description": "Software Vulnerabilities documented by GitHub Security Advisories",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for the returned topics.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "SecurityVulnerabilityOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"UPDATED_AT\",direction:\"DESC\"}"
+                },
+                {
+                  "name": "ecosystem",
+                  "description": "An ecosystem to filter vulnerabilities by.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "SecurityAdvisoryEcosystem",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "package",
+                  "description": "A package name to filter vulnerabilities by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "severities",
+                  "description": "A list of severities to filter vulnerabilities by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "SecurityAdvisorySeverity",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityVulnerabilityConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "topic",
+              "description": "Look up a topic by name.",
+              "args": [
+                {
+                  "name": "name",
+                  "description": "The topic's name.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Topic",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "Lookup a user by login.",
+              "args": [
+                {
+                  "name": "login",
+                  "description": "The user's login.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewer",
+              "description": "The currently authenticated user.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Node",
+          "description": "An object with an ID.",
+          "fields": [
+            {
+              "name": "id",
+              "description": "ID of the object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "AddedToProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "App",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "AssignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "BaseRefChangedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "BaseRefForcePushedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Blob",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Bot",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "BranchProtectionRule",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ClosedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CodeOfConduct",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CommentDeletedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Commit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CommitComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CommitCommentThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ConvertedNoteToIssueEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CrossReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DemilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DeployKey",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DeployedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Deployment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DeploymentEnvironmentChangedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DeploymentStatus",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ExternalIdentity",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Gist",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "GistComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefDeletedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefForcePushedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefRestoredEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Label",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Language",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "License",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Mannequin",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MarketplaceCategory",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MarketplaceListing",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MentionedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MergedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Milestone",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MovedColumnsInProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "OrganizationIdentityProvider",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "OrganizationInvitation",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PinnedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Project",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ProjectCard",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ProjectColumn",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PublicKey",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestCommit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestCommitCommentThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PushAllowance",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Reaction",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Ref",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Release",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReleaseAsset",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RemovedFromProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RenamedTitleEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReopenedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RepositoryInvitation",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RepositoryTopic",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewDismissalAllowance",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewDismissedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewRequestRemovedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewRequestedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "SecurityAdvisory",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Status",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "StatusContext",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "SubscribedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Tag",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Team",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Topic",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "TransferredEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Tree",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnassignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnpinnedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnsubscribedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UserBlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UserContentEdit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UserStatus",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "SCALAR",
+          "name": "ID",
+          "description": "Represents a unique identifier that is Base64 obfuscated. It is often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as `\"VXNlci0xMA==\"`) or integer (such as `4`) input value will be accepted as an ID.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "UniformResourceLocatable",
+          "description": "Represents a type that can be retrieved by a URL.",
+          "fields": [
+            {
+              "name": "resourcePath",
+              "description": "The HTML path to this resource.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The URL to this resource.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Bot",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ClosedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Commit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CrossReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Mannequin",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MergedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Milestone",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestCommit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Release",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RepositoryTopic",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewDismissedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "SCALAR",
+          "name": "URI",
+          "description": "An RFC 3986, RFC 3987, and RFC 6570 (level 4) compliant URI string.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "User",
+          "description": "A user is an individual's account on GitHub that owns repositories and can make new content.",
+          "fields": [
+            {
+              "name": "anyPinnableItems",
+              "description": "Determine if this repository owner has any items that can be pinned to their profile.",
+              "args": [
+                {
+                  "name": "type",
+                  "description": "Filter to only a particular kind of pinnable item.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "PinnableItemType",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "avatarUrl",
+              "description": "A URL pointing to the user's public avatar.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bio",
+              "description": "The user's public profile bio.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bioHTML",
+              "description": "The user's public profile bio as HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitComments",
+              "description": "A list of commit comments made by this user.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "company",
+              "description": "The user's public profile company.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "companyHTML",
+              "description": "The user's public profile company as HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "contributionsCollection",
+              "description": "The collection of contributions this user has made to different repositories.",
+              "args": [
+                {
+                  "name": "organizationID",
+                  "description": "The ID of the organization used to filter contributions.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "from",
+                  "description": "Only contributions made at this time or later will be counted. If omitted, defaults to a year ago.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "DateTime",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "to",
+                  "description": "Only contributions made before and up to and including this time will be counted. If omitted, defaults to the current time.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "DateTime",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ContributionsCollection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "email",
+              "description": "The user's publicly visible profile email.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "followers",
+              "description": "A list of users the given user is followed by.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "FollowerConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "following",
+              "description": "A list of users the given user is following.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "FollowingConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "gist",
+              "description": "Find gist by repo name.",
+              "args": [
+                {
+                  "name": "name",
+                  "description": "The gist name to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Gist",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "gistComments",
+              "description": "A list of gist comments made by this user.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "GistCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "gists",
+              "description": "A list of the Gists the user has created.",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "Filters Gists according to privacy.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "GistPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for gists returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "GistOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "GistConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isBountyHunter",
+              "description": "Whether or not this user is a participant in the GitHub Security Bug Bounty.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isCampusExpert",
+              "description": "Whether or not this user is a participant in the GitHub Campus Experts Program.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isDeveloperProgramMember",
+              "description": "Whether or not this user is a GitHub Developer Program member.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isEmployee",
+              "description": "Whether or not this user is a GitHub employee.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isHireable",
+              "description": "Whether or not the user has marked themselves as for hire.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isSiteAdmin",
+              "description": "Whether or not this user is a site administrator.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isViewer",
+              "description": "Whether or not this user is the viewing user.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issueComments",
+              "description": "A list of issue comments made by this user.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issues",
+              "description": "A list of issues associated with this user.",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for issues returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the issues by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "IssueState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "filterBy",
+                  "description": "Filtering options for issues returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueFilters",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "itemShowcase",
+              "description": "Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProfileItemShowcase",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "location",
+              "description": "The user's public profile location.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "login",
+              "description": "The username used to login.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The user's public profile name.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organization",
+              "description": "Find an organization by its login that the user belongs to.",
+              "args": [
+                {
+                  "name": "login",
+                  "description": "The login of the organization to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Organization",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organizations",
+              "description": "A list of organizations the user belongs to.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "OrganizationConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnableItems",
+              "description": "A list of repositories and gists this profile owner can pin to their profile.",
+              "args": [
+                {
+                  "name": "types",
+                  "description": "Filter the types of pinnable items that are returned.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PinnableItemType",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PinnableItemConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedItems",
+              "description": "A list of repositories and gists this profile owner has pinned to their profile",
+              "args": [
+                {
+                  "name": "types",
+                  "description": "Filter the types of pinned items that are returned.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PinnableItemType",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PinnableItemConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedItemsRemaining",
+              "description": "Returns how many more items this profile owner can pin to their profile.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedRepositories",
+              "description": "A list of repositories this user has pinned to their profile",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "affiliations",
+                  "description": "Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "ownerAffiliations",
+                  "description": "Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "pinnedRepositories will be removed Use ProfileOwner.pinnedItems instead. Removal on 2019-07-01 UTC."
+            },
+            {
+              "name": "project",
+              "description": "Find project by number.",
+              "args": [
+                {
+                  "name": "number",
+                  "description": "The project number to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projects",
+              "description": "A list of projects under the owner.",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for projects returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ProjectOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "search",
+                  "description": "Query to search projects by, currently only searching by name.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the projects by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "ProjectState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectsResourcePath",
+              "description": "The HTTP path listing user's projects",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectsUrl",
+              "description": "The HTTP URL listing user's projects",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publicKeys",
+              "description": "A list of public keys associated with this user.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PublicKeyConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequests",
+              "description": "A list of pull requests associated with this user.",
+              "args": [
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PullRequestState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "headRefName",
+                  "description": "The head ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "baseRefName",
+                  "description": "The base ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for pull requests returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositories",
+              "description": "A list of repositories that the user owns.",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "affiliations",
+                  "description": "Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "ownerAffiliations",
+                  "description": "Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "isFork",
+                  "description": "If non-null, filters repositories according to whether they are forks of another repository",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositoriesContributedTo",
+              "description": "A list of repositories that the user recently contributed to.",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "includeUserRepositories",
+                  "description": "If true, include user repositories",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "contributionTypes",
+                  "description": "If non-null, include only the specified types of contributions. The GitHub.com UI uses [COMMIT, ISSUE, PULL_REQUEST, REPOSITORY]",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryContributionType",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "Find Repository.",
+              "args": [
+                {
+                  "name": "name",
+                  "description": "Name of Repository to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this user",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "starredRepositories",
+              "description": "Repositories the user has starred.",
+              "args": [
+                {
+                  "name": "ownedByViewer",
+                  "description": "Filters starred repositories to only return repositories owned by the viewer.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "StarOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "StarredRepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "status",
+              "description": "The user's description of what they're currently doing.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserStatus",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this user",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanChangePinnedItems",
+              "description": "Can the viewer pin repositories and gists to the profile?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanCreateProjects",
+              "description": "Can the current viewer create new projects on this owner.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanFollow",
+              "description": "Whether or not the viewer is able to follow the user.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerIsFollowing",
+              "description": "Whether or not this user is followed by the viewer.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "watching",
+              "description": "A list of repositories the given user is watching.",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "affiliations",
+                  "description": "Affiliation options for repositories returned from the connection",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\", \"ORGANIZATION_MEMBER\"]"
+                },
+                {
+                  "name": "ownerAffiliations",
+                  "description": "Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "websiteUrl",
+              "description": "A URL pointing to the user's public website/blog.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Actor",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RegistryPackageOwner",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RegistryPackageSearch",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "ProjectOwner",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryOwner",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "ProfileOwner",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Actor",
+          "description": "Represents an object which can take actions on GitHub. Typically a User or Bot.",
+          "fields": [
+            {
+              "name": "avatarUrl",
+              "description": "A URL pointing to the actor's public avatar.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "login",
+              "description": "The username of the actor.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this actor.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this actor.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Bot",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Mannequin",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "SCALAR",
+          "name": "Int",
+          "description": "Represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PageInfo",
+          "description": "Information about pagination in a connection.",
+          "fields": [
+            {
+              "name": "endCursor",
+              "description": "When paginating forwards, the cursor to continue.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasNextPage",
+              "description": "When paginating forwards, are there more items?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasPreviousPage",
+              "description": "When paginating backwards, are there more items?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "startCursor",
+              "description": "When paginating backwards, the cursor to continue.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "SCALAR",
+          "name": "DateTime",
+          "description": "An ISO-8601 encoded UTC date string.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "RegistryPackageOwner",
+          "description": "Represents an owner of a registry package.",
+          "fields": [
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Repository",
+          "description": "A repository contains the content for a project.",
+          "fields": [
+            {
+              "name": "assignableUsers",
+              "description": "A list of users that can be assigned to issues in this repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "branchProtectionRules",
+              "description": "A list of branch protection rules for this repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "BranchProtectionRuleConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "codeOfConduct",
+              "description": "Returns the code of conduct for this repository",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CodeOfConduct",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "collaborators",
+              "description": "A list of collaborators associated with the repository.",
+              "args": [
+                {
+                  "name": "affiliation",
+                  "description": "Collaborators affiliation level with a repository.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "CollaboratorAffiliation",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RepositoryCollaboratorConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitComments",
+              "description": "A list of commit comments associated with the repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "defaultBranchRef",
+              "description": "The Ref associated with the repository's default branch.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deployKeys",
+              "description": "A list of deploy keys that are on this repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "DeployKeyConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deployments",
+              "description": "Deployments associated with the repository",
+              "args": [
+                {
+                  "name": "environments",
+                  "description": "Environments to list deployments for",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for deployments returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "DeploymentOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"CREATED_AT\",direction:\"ASC\"}"
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "DeploymentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "The description of the repository.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "descriptionHTML",
+              "description": "The description of the repository rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "diskUsage",
+              "description": "The number of kilobytes this repository occupies on disk.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "forkCount",
+              "description": "Returns how many forks there are of this repository in the whole network.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "forks",
+              "description": "A list of direct forked repositories.",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "affiliations",
+                  "description": "Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "ownerAffiliations",
+                  "description": "Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasIssuesEnabled",
+              "description": "Indicates if the repository has issues feature enabled.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasWikiEnabled",
+              "description": "Indicates if the repository has wiki feature enabled.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "homepageUrl",
+              "description": "The repository's URL.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isArchived",
+              "description": "Indicates if the repository is unmaintained.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isDisabled",
+              "description": "Returns whether or not this repository disabled.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isFork",
+              "description": "Identifies if the repository is a fork.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isLocked",
+              "description": "Indicates if the repository has been locked or not.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isMirror",
+              "description": "Identifies if the repository is a mirror.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isPrivate",
+              "description": "Identifies if the repository is private.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "Returns a single issue from the current repository by number.",
+              "args": [
+                {
+                  "name": "number",
+                  "description": "The number for the issue to be returned.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Issue",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issueOrPullRequest",
+              "description": "Returns a single issue-like object from the current repository by number.",
+              "args": [
+                {
+                  "name": "number",
+                  "description": "The number for the issue to be returned.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "UNION",
+                "name": "IssueOrPullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issues",
+              "description": "A list of issues that have been opened in the repository.",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for issues returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the issues by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "IssueState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "filterBy",
+                  "description": "Filtering options for issues returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueFilters",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "label",
+              "description": "Returns a single label by name",
+              "args": [
+                {
+                  "name": "name",
+                  "description": "Label name",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Label",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "labels",
+              "description": "A list of labels associated with the repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "query",
+                  "description": "If provided, searches labels by name and description.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "LabelConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "languages",
+              "description": "A list containing a breakdown of the language composition of the repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "LanguageOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "LanguageConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "licenseInfo",
+              "description": "The license associated with the repository",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "License",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lockReason",
+              "description": "The reason the repository has been locked.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "RepositoryLockReason",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mentionableUsers",
+              "description": "A list of Users that can be mentioned in the context of the repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mergeCommitAllowed",
+              "description": "Whether or not PRs are merged with a merge commit on this repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "milestone",
+              "description": "Returns a single milestone from the current repository by number.",
+              "args": [
+                {
+                  "name": "number",
+                  "description": "The number for the milestone to be returned.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Milestone",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "milestones",
+              "description": "A list of milestones associated with the repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "Filter by the state of the milestones.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "MilestoneState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for milestones.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "MilestoneOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "MilestoneConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mirrorUrl",
+              "description": "The repository's original mirror URL.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nameWithOwner",
+              "description": "The repository's name with owner.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "object",
+              "description": "A Git object in the repository",
+              "args": [
+                {
+                  "name": "oid",
+                  "description": "The Git object ID",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "GitObjectID",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "expression",
+                  "description": "A Git revision expression suitable for rev-parse",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "GitObject",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "owner",
+              "description": "The User owner of the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "RepositoryOwner",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "parent",
+              "description": "The repository parent, if this is a fork.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "primaryLanguage",
+              "description": "The primary language of the repository's code.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Language",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "Find project by number.",
+              "args": [
+                {
+                  "name": "number",
+                  "description": "The project number to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projects",
+              "description": "A list of projects under the owner.",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for projects returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ProjectOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "search",
+                  "description": "Query to search projects by, currently only searching by name.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the projects by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "ProjectState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectsResourcePath",
+              "description": "The HTTP path listing the repository's projects",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectsUrl",
+              "description": "The HTTP URL listing the repository's projects",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "Returns a single pull request from the current repository by number.",
+              "args": [
+                {
+                  "name": "number",
+                  "description": "The number for the pull request to be returned.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequests",
+              "description": "A list of pull requests that have been opened in the repository.",
+              "args": [
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PullRequestState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "headRefName",
+                  "description": "The head ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "baseRefName",
+                  "description": "The base ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for pull requests returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pushedAt",
+              "description": "Identifies when the repository was last pushed to.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "rebaseMergeAllowed",
+              "description": "Whether or not rebase-merging is enabled on this repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ref",
+              "description": "Fetch a given ref from the repository",
+              "args": [
+                {
+                  "name": "qualifiedName",
+                  "description": "The ref to retrieve. Fully qualified matches are checked in order (`refs/heads/master`) before falling back onto checks for short name matches (`master`).",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "refs",
+              "description": "Fetch a list of refs from the repository",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "refPrefix",
+                  "description": "A ref name prefix like `refs/heads/`, `refs/tags/`, etc.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "direction",
+                  "description": "DEPRECATED: use orderBy. The ordering direction.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "OrderDirection",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for refs returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RefOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RefConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "release",
+              "description": "Lookup a single release given various criteria.",
+              "args": [
+                {
+                  "name": "tagName",
+                  "description": "The name of the Tag the Release was created from",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Release",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "releases",
+              "description": "List of releases which are dependent on this repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ReleaseOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReleaseConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositoryTopics",
+              "description": "A list of applied repository-topic associations for this repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryTopicConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this repository",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "shortDescriptionHTML",
+              "description": "A description of the repository, rendered to HTML without any links in it.",
+              "args": [
+                {
+                  "name": "limit",
+                  "description": "How many characters to return.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "200"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "squashMergeAllowed",
+              "description": "Whether or not squash-merging is enabled on this repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "sshUrl",
+              "description": "The SSH URL to clone this repository",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitSSHRemote",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "stargazers",
+              "description": "A list of users who have starred this starrable.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "StarOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "StargazerConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this repository",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanAdminister",
+              "description": "Indicates whether the viewer has admin permissions on this repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanCreateProjects",
+              "description": "Can the current viewer create new projects on this owner.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanSubscribe",
+              "description": "Check if the viewer is able to change their subscription status for the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdateTopics",
+              "description": "Indicates whether the viewer can update the topics of this repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerHasStarred",
+              "description": "Returns a boolean indicating whether the viewing user has starred this starrable.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerPermission",
+              "description": "The users permission level on the repository. Will return null if authenticated as an GitHub App.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "RepositoryPermission",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerSubscription",
+              "description": "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "SubscriptionState",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "watchers",
+              "description": "A list of users watching the repository.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "ProjectOwner",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RegistryPackageOwner",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Subscribable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Starrable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryInfo",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "ProjectOwner",
+          "description": "Represents an owner of a Project.",
+          "fields": [
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "Find project by number.",
+              "args": [
+                {
+                  "name": "number",
+                  "description": "The project number to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projects",
+              "description": "A list of projects under the owner.",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for projects returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ProjectOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "search",
+                  "description": "Query to search projects by, currently only searching by name.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the projects by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "ProjectState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectsResourcePath",
+              "description": "The HTTP path listing owners projects",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectsUrl",
+              "description": "The HTTP URL listing owners projects",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanCreateProjects",
+              "description": "Can the current viewer create new projects on this owner.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Project",
+          "description": "Projects manage issues, pull requests and notes within a project owner.",
+          "fields": [
+            {
+              "name": "body",
+              "description": "The project's description body.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "The projects description body rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closed",
+              "description": "`true` if the object is closed (definition of closed may depend on type)",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closedAt",
+              "description": "Identifies the date and time when the object was closed.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "columns",
+              "description": "List of columns in the project",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectColumnConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "creator",
+              "description": "The actor who originally created the project.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The project's name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "number",
+              "description": "The project's number.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "owner",
+              "description": "The project's owner. Currently limited to repositories, organizations, and users.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "ProjectOwner",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pendingCards",
+              "description": "List of pending cards in this project",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "archivedStates",
+                  "description": "A list of archived states to filter the cards by",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "ProjectCardArchivedState",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"ARCHIVED\", \"NOT_ARCHIVED\"]"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectCardConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this project",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "Whether the project is open or closed.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ProjectState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this project",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Closable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Updatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Closable",
+          "description": "An object that can be closed",
+          "fields": [
+            {
+              "name": "closed",
+              "description": "`true` if the object is closed (definition of closed may depend on type)",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closedAt",
+              "description": "Identifies the date and time when the object was closed.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Milestone",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Project",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Updatable",
+          "description": "Entities that can be updated.",
+          "fields": [
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CommitComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "GistComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Project",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewComment",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "ENUM",
+          "name": "ProjectState",
+          "description": "State of the project; either 'open' or 'closed'",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OPEN",
+              "description": "The project is open.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CLOSED",
+              "description": "The project is closed.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "SCALAR",
+          "name": "HTML",
+          "description": "A string containing HTML code.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProjectColumnConnection",
+          "description": "The connection type for ProjectColumn.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectColumnEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectColumn",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProjectColumnEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectColumn",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProjectColumn",
+          "description": "A column inside a project.",
+          "fields": [
+            {
+              "name": "cards",
+              "description": "List of cards in the column",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "archivedStates",
+                  "description": "A list of archived states to filter the cards by",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "ProjectCardArchivedState",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"ARCHIVED\", \"NOT_ARCHIVED\"]"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectCardConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The project column's name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "The project that contains this column.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Project",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "purpose",
+              "description": "The semantic purpose of the column",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "ProjectColumnPurpose",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this project column",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this project column",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ProjectColumnPurpose",
+          "description": "The semantic purpose of the column - todo, in progress, or done.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "TODO",
+              "description": "The column contains cards still to be worked on",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "IN_PROGRESS",
+              "description": "The column contains cards which are currently being worked on",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DONE",
+              "description": "The column contains cards which are complete",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProjectCardConnection",
+          "description": "The connection type for ProjectCard.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectCardEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectCard",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProjectCardEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectCard",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProjectCard",
+          "description": "A card in a project.",
+          "fields": [
+            {
+              "name": "column",
+              "description": "The project column this card is associated under. A card may only belong to one\nproject column at a time. The column field will be null if the card is created\nin a pending state and has yet to be associated with a column. Once cards are\nassociated with a column, they will not become pending in the future.\n",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectColumn",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "content",
+              "description": "The card content item",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "ProjectCardItem",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "creator",
+              "description": "The actor who created this card",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isArchived",
+              "description": "Whether the card is archived",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "note",
+              "description": "The card note",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "The project that contains this card.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Project",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this card",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "The state of ProjectCard",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "ProjectCardState",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this card",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ProjectCardState",
+          "description": "Various content states of a ProjectCard",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CONTENT_ONLY",
+              "description": "The card has content only.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NOTE_ONLY",
+              "description": "The card has a note only.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REDACTED",
+              "description": "The card is redacted.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "ProjectCardItem",
+          "description": "Types that can be inside Project Cards.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Issue",
+          "description": "An Issue is a place to discuss ideas, enhancements, tasks, and bugs for a project.",
+          "fields": [
+            {
+              "name": "activeLockReason",
+              "description": "Reason that the conversation was locked.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "LockReason",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "assignees",
+              "description": "A list of Users assigned to this object.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "author",
+              "description": "The actor who authored the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authorAssociation",
+              "description": "Author's association with the subject of the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommentAuthorAssociation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "body",
+              "description": "Identifies the body of the issue.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "Identifies the body of the issue rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyText",
+              "description": "Identifies the body of the issue rendered to text.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closed",
+              "description": "`true` if the object is closed (definition of closed may depend on type)",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closedAt",
+              "description": "Identifies the date and time when the object was closed.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "comments",
+              "description": "A list of comments associated with the Issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdViaEmail",
+              "description": "Check if this comment was created via an email reply.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "includesCreatedEdit",
+              "description": "Check if this comment was edited and includes an edit with the creation data",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "labels",
+              "description": "A list of labels associated with the object.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "LabelConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastEditedAt",
+              "description": "The moment the editor made the last edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "locked",
+              "description": "`true` if the object is locked",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "milestone",
+              "description": "Identifies the milestone associated with the issue.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Milestone",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "number",
+              "description": "Identifies the issue number.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "participants",
+              "description": "A list of Users that are participating in the Issue conversation.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectCards",
+              "description": "List of project cards associated with this issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "archivedStates",
+                  "description": "A list of archived states to filter the cards by",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "ProjectCardArchivedState",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"ARCHIVED\", \"NOT_ARCHIVED\"]"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectCardConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies when the comment was published at.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactionGroups",
+              "description": "A list of reactions grouped by content left on the subject.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "ReactionGroup",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactions",
+              "description": "A list of Reactions left on the Issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "content",
+                  "description": "Allows filtering Reactions by emoji.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "ReactionContent",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Allows specifying the order in which reactions are returned.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ReactionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this issue",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "Identifies the state of the issue.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "IssueState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "timeline",
+              "description": "A list of events, comments, commits, etc. associated with the issue.",
+              "args": [
+                {
+                  "name": "since",
+                  "description": "Allows filtering timeline events by a `since` timestamp.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "DateTime",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueTimelineConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "timelineItems",
+              "description": "A list of events, comments, commits, etc. associated with the issue.",
+              "args": [
+                {
+                  "name": "since",
+                  "description": "Filter timeline items by a `since` timestamp.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "DateTime",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "skip",
+                  "description": "Skips the first _n_ elements in the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "itemTypes",
+                  "description": "Filter timeline items by type.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "IssueTimelineItemsItemType",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueTimelineItemsConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "title",
+              "description": "Identifies the issue title.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this issue",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userContentEdits",
+              "description": "A list of edits to this content.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEditConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanReact",
+              "description": "Can user react to this subject",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanSubscribe",
+              "description": "Check if the viewer is able to change their subscription status for the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCannotUpdateReasons",
+              "description": "Reasons why the current viewer can not update this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "CommentCannotUpdateReason",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerDidAuthor",
+              "description": "Did the viewer author this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerSubscription",
+              "description": "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "SubscriptionState",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Assignable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Closable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Comment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Updatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UpdatableComment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Labelable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Lockable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Reactable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryNode",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Subscribable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Assignable",
+          "description": "An object that can have users assigned to it.",
+          "fields": [
+            {
+              "name": "assignees",
+              "description": "A list of Users assigned to this object.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserConnection",
+          "description": "The connection type for User.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserEdge",
+          "description": "Represents a user.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Comment",
+          "description": "Represents a comment.",
+          "fields": [
+            {
+              "name": "author",
+              "description": "The actor who authored the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authorAssociation",
+              "description": "Author's association with the subject of the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommentAuthorAssociation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "body",
+              "description": "The body as Markdown.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "The body rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyText",
+              "description": "The body rendered to text.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdViaEmail",
+              "description": "Check if this comment was created via an email reply.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "includesCreatedEdit",
+              "description": "Check if this comment was edited and includes an edit with the creation data",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastEditedAt",
+              "description": "The moment the editor made the last edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies when the comment was published at.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userContentEdits",
+              "description": "A list of edits to this content.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEditConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerDidAuthor",
+              "description": "Did the viewer author this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CommitComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "GistComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewComment",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserContentEditConnection",
+          "description": "A list of edits to content.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserContentEditEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserContentEdit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserContentEditEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEdit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserContentEdit",
+          "description": "An edit on user content",
+          "fields": [
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletedAt",
+              "description": "Identifies the date and time when the object was deleted.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletedBy",
+              "description": "The actor who deleted this content",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "diff",
+              "description": "A summary of the changes for this edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editedAt",
+              "description": "When this content was edited",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited this content",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "CommentAuthorAssociation",
+          "description": "A comment author association with repository.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "MEMBER",
+              "description": "Author is a member of the organization that owns the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OWNER",
+              "description": "Author is the owner of the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "COLLABORATOR",
+              "description": "Author has been invited to collaborate on the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CONTRIBUTOR",
+              "description": "Author has previously committed to the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FIRST_TIME_CONTRIBUTOR",
+              "description": "Author has not previously committed to the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FIRST_TIMER",
+              "description": "Author has not previously committed to GitHub.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NONE",
+              "description": "Author has no association with the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "UpdatableComment",
+          "description": "Comments that can be updated.",
+          "fields": [
+            {
+              "name": "viewerCannotUpdateReasons",
+              "description": "Reasons why the current viewer can not update this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "CommentCannotUpdateReason",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CommitComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "GistComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewComment",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "ENUM",
+          "name": "CommentCannotUpdateReason",
+          "description": "The possible errors that will prevent a user from updating a comment.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "INSUFFICIENT_ACCESS",
+              "description": "You must be the author or have write access to this repository to update this comment.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "LOCKED",
+              "description": "Unable to create comment because issue is locked.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "LOGIN_REQUIRED",
+              "description": "You must be logged in to update this comment.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MAINTENANCE",
+              "description": "Repository is under maintenance.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "VERIFIED_EMAIL_REQUIRED",
+              "description": "At least one email address must be verified to update this comment.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DENIED",
+              "description": "You cannot update this comment",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Labelable",
+          "description": "An object that can have labels assigned to it.",
+          "fields": [
+            {
+              "name": "labels",
+              "description": "A list of labels associated with the object.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "LabelConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "LabelConnection",
+          "description": "The connection type for Label.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "LabelEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Label",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "LabelEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Label",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Label",
+          "description": "A label for categorizing Issues or Milestones with a given Repository.",
+          "fields": [
+            {
+              "name": "color",
+              "description": "Identifies the label color.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the label was created.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "A brief description of this label.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isDefault",
+              "description": "Indicates whether or not this is a default label.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issues",
+              "description": "A list of issues associated with this label.",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for issues returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the issues by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "IssueState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "filterBy",
+                  "description": "Filtering options for issues returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueFilters",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "Identifies the label name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequests",
+              "description": "A list of pull requests associated with this label.",
+              "args": [
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PullRequestState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "headRefName",
+                  "description": "The head ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "baseRefName",
+                  "description": "The base ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for pull requests returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this label.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this label.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the label was last updated.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this label.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueConnection",
+          "description": "The connection type for Issue.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Issue",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Issue",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "IssueOrder",
+          "description": "Ways in which lists of issues can be ordered upon return.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field in which to order issues by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "IssueOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The direction in which to order issues by the specified field.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "IssueOrderField",
+          "description": "Properties by which issue connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Order issues by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UPDATED_AT",
+              "description": "Order issues by update time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "COMMENTS",
+              "description": "Order issues by comment count",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "OrderDirection",
+          "description": "Possible directions in which to order a list of items when provided an `orderBy` argument.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ASC",
+              "description": "Specifies an ascending order for a given `orderBy` argument.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DESC",
+              "description": "Specifies a descending order for a given `orderBy` argument.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "IssueState",
+          "description": "The possible states of an issue.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OPEN",
+              "description": "An issue that is still open",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CLOSED",
+              "description": "An issue that has been closed",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "IssueFilters",
+          "description": "Ways in which to filter lists of issues.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "assignee",
+              "description": "List issues assigned to given name. Pass in `null` for issues with no assigned user, and `*` for issues assigned to any user.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "createdBy",
+              "description": "List issues created by given name.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "labels",
+              "description": "List issues where the list of label names exist on the issue.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "mentioned",
+              "description": "List issues where the given name is mentioned in the issue.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "milestone",
+              "description": "List issues by given milestone argument. If an string representation of an integer is passed, it should refer to a milestone by its number field. Pass in `null` for issues with no milestone, and `*` for issues that are assigned to any milestone.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "since",
+              "description": "List issues that have been updated at or after the given date.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "states",
+              "description": "List issues filtered by the list of states given.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "ENUM",
+                    "name": "IssueState",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "viewerSubscribed",
+              "description": "List issues subscribed to by viewer.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": "false"
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestConnection",
+          "description": "The connection type for PullRequest.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequest",
+          "description": "A repository pull request.",
+          "fields": [
+            {
+              "name": "activeLockReason",
+              "description": "Reason that the conversation was locked.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "LockReason",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "additions",
+              "description": "The number of additions in this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "assignees",
+              "description": "A list of Users assigned to this object.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "author",
+              "description": "The actor who authored the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authorAssociation",
+              "description": "Author's association with the subject of the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommentAuthorAssociation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "baseRef",
+              "description": "Identifies the base Ref associated with the pull request.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "baseRefName",
+              "description": "Identifies the name of the base Ref associated with the pull request, even if the ref has been deleted.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "baseRefOid",
+              "description": "Identifies the oid of the base ref associated with the pull request, even if the ref has been deleted.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "baseRepository",
+              "description": "The repository associated with this pull request's base Ref.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "body",
+              "description": "The body as Markdown.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "The body rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyText",
+              "description": "The body rendered to text.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "changedFiles",
+              "description": "The number of changed files in this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closed",
+              "description": "`true` if the pull request is closed",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closedAt",
+              "description": "Identifies the date and time when the object was closed.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "comments",
+              "description": "A list of comments associated with the pull request.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commits",
+              "description": "A list of commits present in this pull request's head branch not present in the base branch.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestCommitConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdViaEmail",
+              "description": "Check if this comment was created via an email reply.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletions",
+              "description": "The number of deletions in this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited this pull request's body.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "files",
+              "description": "Lists the files changed within this pull request.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestChangedFileConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "headRef",
+              "description": "Identifies the head Ref associated with the pull request.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "headRefName",
+              "description": "Identifies the name of the head Ref associated with the pull request, even if the ref has been deleted.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "headRefOid",
+              "description": "Identifies the oid of the head ref associated with the pull request, even if the ref has been deleted.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "headRepository",
+              "description": "The repository associated with this pull request's head Ref.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "headRepositoryOwner",
+              "description": "The owner of the repository associated with this pull request's head Ref.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "RepositoryOwner",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "includesCreatedEdit",
+              "description": "Check if this comment was edited and includes an edit with the creation data",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isCrossRepository",
+              "description": "The head and base repositories are different.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "labels",
+              "description": "A list of labels associated with the object.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "LabelConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastEditedAt",
+              "description": "The moment the editor made the last edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "locked",
+              "description": "`true` if the pull request is locked",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "maintainerCanModify",
+              "description": "Indicates whether maintainers can modify the pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mergeCommit",
+              "description": "The commit that was created when this pull request was merged.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mergeable",
+              "description": "Whether or not the pull request can be merged based on the existence of merge conflicts.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "MergeableState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "merged",
+              "description": "Whether or not the pull request was merged.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mergedAt",
+              "description": "The date and time that the pull request was merged.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mergedBy",
+              "description": "The actor who merged the pull request.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "milestone",
+              "description": "Identifies the milestone associated with the pull request.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Milestone",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "number",
+              "description": "Identifies the pull request number.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "participants",
+              "description": "A list of Users that are participating in the Pull Request conversation.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "permalink",
+              "description": "The permalink to the pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "potentialMergeCommit",
+              "description": "The commit that GitHub automatically generated to test if this pull request could be merged. This field will not return a value if the pull request is merged, or if the test merge commit is still being generated. See the `mergeable` field for more details on the mergeability of the pull request.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectCards",
+              "description": "List of project cards associated with this pull request.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "archivedStates",
+                  "description": "A list of archived states to filter the cards by",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "ProjectCardArchivedState",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"ARCHIVED\", \"NOT_ARCHIVED\"]"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectCardConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies when the comment was published at.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactionGroups",
+              "description": "A list of reactions grouped by content left on the subject.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "ReactionGroup",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactions",
+              "description": "A list of Reactions left on the Issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "content",
+                  "description": "Allows filtering Reactions by emoji.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "ReactionContent",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Allows specifying the order in which reactions are returned.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ReactionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "revertResourcePath",
+              "description": "The HTTP path for reverting this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "revertUrl",
+              "description": "The HTTP URL for reverting this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reviewRequests",
+              "description": "A list of review requests associated with the pull request.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ReviewRequestConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reviewThreads",
+              "description": "The list of all review threads for this pull request.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReviewThreadConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reviews",
+              "description": "A list of reviews associated with the pull request.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the reviews.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PullRequestReviewState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "author",
+                  "description": "Filter by author of the review.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "Identifies the state of the pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "PullRequestState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "suggestedReviewers",
+              "description": "A list of reviewer suggestions based on commit history and past review comments.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "SuggestedReviewer",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "timeline",
+              "description": "A list of events, comments, commits, etc. associated with the pull request.",
+              "args": [
+                {
+                  "name": "since",
+                  "description": "Allows filtering timeline events by a `since` timestamp.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "DateTime",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestTimelineConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "timelineItems",
+              "description": "A list of events, comments, commits, etc. associated with the pull request.",
+              "args": [
+                {
+                  "name": "since",
+                  "description": "Filter timeline items by a `since` timestamp.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "DateTime",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "skip",
+                  "description": "Skips the first _n_ elements in the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "itemTypes",
+                  "description": "Filter timeline items by type.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PullRequestTimelineItemsItemType",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestTimelineItemsConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "title",
+              "description": "Identifies the pull request title.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userContentEdits",
+              "description": "A list of edits to this content.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEditConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanApplySuggestion",
+              "description": "Whether or not the viewer can apply suggestion.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanReact",
+              "description": "Can user react to this subject",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanSubscribe",
+              "description": "Check if the viewer is able to change their subscription status for the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCannotUpdateReasons",
+              "description": "Reasons why the current viewer can not update this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "CommentCannotUpdateReason",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerDidAuthor",
+              "description": "Did the viewer author this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerSubscription",
+              "description": "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "SubscriptionState",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Assignable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Closable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Comment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Updatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UpdatableComment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Labelable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Lockable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Reactable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryNode",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Subscribable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Lockable",
+          "description": "An object that can be locked.",
+          "fields": [
+            {
+              "name": "activeLockReason",
+              "description": "Reason that the conversation was locked.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "LockReason",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "locked",
+              "description": "`true` if the object is locked",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "ENUM",
+          "name": "LockReason",
+          "description": "The possible reasons that an issue or pull request was locked.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OFF_TOPIC",
+              "description": "The issue or pull request was locked because the conversation was off-topic.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "TOO_HEATED",
+              "description": "The issue or pull request was locked because the conversation was too heated.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "RESOLVED",
+              "description": "The issue or pull request was locked because the conversation was resolved.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SPAM",
+              "description": "The issue or pull request was locked because the conversation was spam.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "App",
+          "description": "A GitHub App.",
+          "fields": [
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "The description of the app.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "logoBackgroundColor",
+              "description": "The hex color code, without the leading '#', for the logo background.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "logoUrl",
+              "description": "A URL pointing to the app's logo.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size of the resulting image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the app.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "slug",
+              "description": "A slug based on the name of the app for use in URLs.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The URL to the app's homepage.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MarketplaceListing",
+          "description": "A listing in the GitHub integration marketplace.",
+          "fields": [
+            {
+              "name": "app",
+              "description": "The GitHub App this listing represents.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "App",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "companyUrl",
+              "description": "URL to the listing owner's company site.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "configurationResourcePath",
+              "description": "The HTTP path for configuring access to the listing's integration or OAuth app",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "configurationUrl",
+              "description": "The HTTP URL for configuring access to the listing's integration or OAuth app",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "documentationUrl",
+              "description": "URL to the listing's documentation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "extendedDescription",
+              "description": "The listing's detailed description.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "extendedDescriptionHTML",
+              "description": "The listing's detailed description rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "fullDescription",
+              "description": "The listing's introductory description.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "fullDescriptionHTML",
+              "description": "The listing's introductory description rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasApprovalBeenRequested",
+              "description": "Whether this listing has been submitted for review from GitHub for approval to be displayed in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "`hasApprovalBeenRequested` will be removed. Use `isVerificationPendingFromDraft` instead. Removal on 2019-10-01 UTC."
+            },
+            {
+              "name": "hasPublishedFreeTrialPlans",
+              "description": "Does this listing have any plans with a free trial?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasTermsOfService",
+              "description": "Does this listing have a terms of service link?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "howItWorks",
+              "description": "A technical description of how this app works with GitHub.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "howItWorksHTML",
+              "description": "The listing's technical description rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "installationUrl",
+              "description": "URL to install the product to the viewer's account or organization.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "installedForViewer",
+              "description": "Whether this listing's app has been installed for the current viewer",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isApproved",
+              "description": "Whether this listing has been approved for display in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "`isApproved` will be removed. Use `isPublic` instead. Removal on 2019-10-01 UTC."
+            },
+            {
+              "name": "isArchived",
+              "description": "Whether this listing has been removed from the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isDelisted",
+              "description": "Whether this listing has been removed from the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "`isDelisted` will be removed. Use `isArchived` instead. Removal on 2019-10-01 UTC."
+            },
+            {
+              "name": "isDraft",
+              "description": "Whether this listing is still an editable draft that has not been submitted for review and is not publicly visible in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isPaid",
+              "description": "Whether the product this listing represents is available as part of a paid plan.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isPublic",
+              "description": "Whether this listing has been approved for display in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isRejected",
+              "description": "Whether this listing has been rejected by GitHub for display in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isUnverified",
+              "description": "Whether this listing has been approved for unverified display in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isUnverifiedPending",
+              "description": "Whether this draft listing has been submitted for review for approval to be unverified in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isVerificationPendingFromDraft",
+              "description": "Whether this draft listing has been submitted for review from GitHub for approval to be verified in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isVerificationPendingFromUnverified",
+              "description": "Whether this unverified listing has been submitted for review from GitHub for approval to be verified in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isVerified",
+              "description": "Whether this listing has been approved for verified display in the Marketplace.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "logoBackgroundColor",
+              "description": "The hex color code, without the leading '#', for the logo background.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "logoUrl",
+              "description": "URL for the listing's logo image.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size in pixels of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "400"
+                }
+              ],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The listing's full name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "normalizedShortDescription",
+              "description": "The listing's very short description without a trailing period or ampersands.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pricingUrl",
+              "description": "URL to the listing's detailed pricing.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "primaryCategory",
+              "description": "The category that best describes the listing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "MarketplaceCategory",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "privacyPolicyUrl",
+              "description": "URL to the listing's privacy policy, may return an empty string for listings that do not require a privacy policy URL.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for the Marketplace listing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "screenshotUrls",
+              "description": "The URLs for the listing's screenshots.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "secondaryCategory",
+              "description": "An alternate category that describes the listing.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "MarketplaceCategory",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "shortDescription",
+              "description": "The listing's very short description.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "slug",
+              "description": "The short name of the listing used in its URL.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "statusUrl",
+              "description": "URL to the listing's status page.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "supportEmail",
+              "description": "An email address for support for this listing's app.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "supportUrl",
+              "description": "Either a URL or an email address for support for this listing's app, may return an empty string for listings that do not require a support URL.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "termsOfServiceUrl",
+              "description": "URL to the listing's terms of service.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for the Marketplace listing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanAddPlans",
+              "description": "Can the current viewer add plans for this Marketplace listing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanApprove",
+              "description": "Can the current viewer approve this Marketplace listing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanDelist",
+              "description": "Can the current viewer delist this Marketplace listing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanEdit",
+              "description": "Can the current viewer edit this Marketplace listing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanEditCategories",
+              "description": "Can the current viewer edit the primary and secondary category of this\nMarketplace listing.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanEditPlans",
+              "description": "Can the current viewer edit the plans for this Marketplace listing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanRedraft",
+              "description": "Can the current viewer return this Marketplace listing to draft state\nso it becomes editable again.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanReject",
+              "description": "Can the current viewer reject this Marketplace listing by returning it to\nan editable draft state or rejecting it entirely.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanRequestApproval",
+              "description": "Can the current viewer request this listing be reviewed for display in\nthe Marketplace as verified.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerHasPurchased",
+              "description": "Indicates whether the current user has an active subscription to this Marketplace listing.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerHasPurchasedForAllOrganizations",
+              "description": "Indicates if the current user has purchased a subscription to this Marketplace listing\nfor all of the organizations the user owns.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerIsListingAdmin",
+              "description": "Does the current viewer role allow them to administer this Marketplace listing.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Organization",
+          "description": "An account on GitHub, with one or more owners, that has repositories, members and teams.",
+          "fields": [
+            {
+              "name": "anyPinnableItems",
+              "description": "Determine if this repository owner has any items that can be pinned to their profile.",
+              "args": [
+                {
+                  "name": "type",
+                  "description": "Filter to only a particular kind of pinnable item.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "PinnableItemType",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "avatarUrl",
+              "description": "A URL pointing to the organization's public avatar.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "The organization's public profile description.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "email",
+              "description": "The organization's public email.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isVerified",
+              "description": "Whether the organization has verified its profile email and website.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "itemShowcase",
+              "description": "Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProfileItemShowcase",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "location",
+              "description": "The organization's public profile location.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "login",
+              "description": "The organization's login name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "memberStatuses",
+              "description": "Get the status messages members of this entity have set that are either public or visible only to the organization.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for user statuses returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "UserStatusOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"UPDATED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserStatusConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "membersWithRole",
+              "description": "A list of users who are members of this organization.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "OrganizationMemberConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The organization's public profile name.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "newTeamResourcePath",
+              "description": "The HTTP path creating a new team",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "newTeamUrl",
+              "description": "The HTTP URL creating a new team",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organizationBillingEmail",
+              "description": "The billing email for the organization.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pendingMembers",
+              "description": "A list of users who have been invited to join this organization.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnableItems",
+              "description": "A list of repositories and gists this profile owner can pin to their profile.",
+              "args": [
+                {
+                  "name": "types",
+                  "description": "Filter the types of pinnable items that are returned.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PinnableItemType",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PinnableItemConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedItems",
+              "description": "A list of repositories and gists this profile owner has pinned to their profile",
+              "args": [
+                {
+                  "name": "types",
+                  "description": "Filter the types of pinned items that are returned.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PinnableItemType",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PinnableItemConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedItemsRemaining",
+              "description": "Returns how many more items this profile owner can pin to their profile.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedRepositories",
+              "description": "A list of repositories this user has pinned to their profile",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "affiliations",
+                  "description": "Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "ownerAffiliations",
+                  "description": "Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "pinnedRepositories will be removed Use ProfileOwner.pinnedItems instead. Removal on 2019-07-01 UTC."
+            },
+            {
+              "name": "project",
+              "description": "Find project by number.",
+              "args": [
+                {
+                  "name": "number",
+                  "description": "The project number to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projects",
+              "description": "A list of projects under the owner.",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for projects returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ProjectOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "search",
+                  "description": "Query to search projects by, currently only searching by name.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the projects by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "ProjectState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectsResourcePath",
+              "description": "The HTTP path listing organization's projects",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectsUrl",
+              "description": "The HTTP URL listing organization's projects",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositories",
+              "description": "A list of repositories that the user owns.",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "affiliations",
+                  "description": "Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "ownerAffiliations",
+                  "description": "Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "isFork",
+                  "description": "If non-null, filters repositories according to whether they are forks of another repository",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "Find Repository.",
+              "args": [
+                {
+                  "name": "name",
+                  "description": "Name of Repository to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requiresTwoFactorAuthentication",
+              "description": "When true the organization requires all members, billing managers, and outside collaborators to enable two-factor authentication.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this organization.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "samlIdentityProvider",
+              "description": "The Organization's SAML identity providers",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "OrganizationIdentityProvider",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "team",
+              "description": "Find an organization's team by its slug.",
+              "args": [
+                {
+                  "name": "slug",
+                  "description": "The name or slug of the team to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Team",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "teams",
+              "description": "A list of teams in this organization.",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters teams according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "TeamPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "role",
+                  "description": "If non-null, filters teams according to whether the viewer is an admin or member on team",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "TeamRole",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "query",
+                  "description": "If non-null, filters teams with query on team name and team slug",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "userLogins",
+                  "description": "User logins to filter by",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for teams returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "TeamOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "ldapMapped",
+                  "description": "If true, filters teams that are mapped to an LDAP Group (Enterprise only)",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "rootTeamsOnly",
+                  "description": "If true, restrict to only root teams",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "teamsResourcePath",
+              "description": "The HTTP path listing organization's teams",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "teamsUrl",
+              "description": "The HTTP URL listing organization's teams",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this organization.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanAdminister",
+              "description": "Organization is adminable by the viewer.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanChangePinnedItems",
+              "description": "Can the viewer pin repositories and gists to the profile?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanCreateProjects",
+              "description": "Can the current viewer create new projects on this owner.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanCreateRepositories",
+              "description": "Viewer can create repositories on this organization",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanCreateTeams",
+              "description": "Viewer can create teams on this organization.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerIsAMember",
+              "description": "Viewer is an active member of this organization.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "websiteUrl",
+              "description": "The organization's public profile URL.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Actor",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RegistryPackageOwner",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RegistryPackageSearch",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "ProjectOwner",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryOwner",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "MemberStatusable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "ProfileOwner",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "RegistryPackageSearch",
+          "description": "Represents an interface to search packages on an object.",
+          "fields": [
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "RepositoryOwner",
+          "description": "Represents an owner of a Repository.",
+          "fields": [
+            {
+              "name": "avatarUrl",
+              "description": "A URL pointing to the owner's public avatar.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "login",
+              "description": "The username used to login.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedRepositories",
+              "description": "A list of repositories this user has pinned to their profile",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "affiliations",
+                  "description": "Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "ownerAffiliations",
+                  "description": "Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "pinnedRepositories will be removed Use ProfileOwner.pinnedItems instead. Removal on 2019-07-01 UTC."
+            },
+            {
+              "name": "repositories",
+              "description": "A list of repositories that the user owns.",
+              "args": [
+                {
+                  "name": "privacy",
+                  "description": "If non-null, filters repositories according to privacy",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "RepositoryPrivacy",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for repositories returned from the connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "RepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "affiliations",
+                  "description": "Array of viewer's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the current viewer owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "ownerAffiliations",
+                  "description": "Array of owner's affiliation options for repositories returned from the connection. For example, OWNER will include only repositories that the organization or user being viewed owns.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "RepositoryAffiliation",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": "[\"OWNER\", \"COLLABORATOR\"]"
+                },
+                {
+                  "name": "isLocked",
+                  "description": "If non-null, filters repositories according to whether they have been locked",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "isFork",
+                  "description": "If non-null, filters repositories according to whether they are forks of another repository",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "Find Repository.",
+              "args": [
+                {
+                  "name": "name",
+                  "description": "Name of Repository to find.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP URL for the owner.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for the owner.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryConnection",
+          "description": "A list of repositories owned by the subject.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalDiskUsage",
+              "description": "The total size in kilobytes of all repositories in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "RepositoryPrivacy",
+          "description": "The privacy of a repository",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "PUBLIC",
+              "description": "Public",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PRIVATE",
+              "description": "Private",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "RepositoryOrder",
+          "description": "Ordering options for repository connections",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order repositories by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "RepositoryOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "RepositoryOrderField",
+          "description": "Properties by which repository connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Order repositories by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UPDATED_AT",
+              "description": "Order repositories by update time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PUSHED_AT",
+              "description": "Order repositories by push time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NAME",
+              "description": "Order repositories by name",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "STARGAZERS",
+              "description": "Order repositories by number of stargazers",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "RepositoryAffiliation",
+          "description": "The affiliation of a user to a repository",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OWNER",
+              "description": "Repositories that are owned by the authenticated user.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "COLLABORATOR",
+              "description": "Repositories that the user has been added to as a collaborator.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ORGANIZATION_MEMBER",
+              "description": "Repositories that the user has access to through being a member of an organization. This includes every repository on every team that the user is on.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "SCALAR",
+          "name": "Float",
+          "description": "Represents signed double-precision fractional values as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point).",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "MemberStatusable",
+          "description": "Entities that have members who can set status messages.",
+          "fields": [
+            {
+              "name": "memberStatuses",
+              "description": "Get the status messages members of this entity have set that are either public or visible only to the organization.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for user statuses returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "UserStatusOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"UPDATED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserStatusConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Team",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserStatusConnection",
+          "description": "The connection type for UserStatus.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserStatusEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserStatus",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserStatusEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserStatus",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserStatus",
+          "description": "The user's description of what they're currently doing.",
+          "fields": [
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "emoji",
+              "description": "An emoji summarizing the user's status.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": "ID of the object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "indicatesLimitedAvailability",
+              "description": "Whether this status indicates the user is not fully available on GitHub.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "message",
+              "description": "A brief message describing what the user is doing.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organization",
+              "description": "The organization whose members can see this status. If null, this status is publicly visible.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Organization",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who has this status.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UserStatusOrder",
+          "description": "Ordering options for user status connections.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order user statuses by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "UserStatusOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "UserStatusOrderField",
+          "description": "Properties by which user status connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "UPDATED_AT",
+              "description": "Order user statuses by when they were updated.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "ProfileOwner",
+          "description": "Represents any entity on GitHub that has a profile page.",
+          "fields": [
+            {
+              "name": "anyPinnableItems",
+              "description": "Determine if this repository owner has any items that can be pinned to their profile.",
+              "args": [
+                {
+                  "name": "type",
+                  "description": "Filter to only a particular kind of pinnable item.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "PinnableItemType",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "email",
+              "description": "The public profile email.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "itemShowcase",
+              "description": "Showcases a selection of repositories and gists that the profile owner has either curated or that have been selected automatically based on popularity.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProfileItemShowcase",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "location",
+              "description": "The public profile location.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "login",
+              "description": "The username used to login.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The public profile name.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnableItems",
+              "description": "A list of repositories and gists this profile owner can pin to their profile.",
+              "args": [
+                {
+                  "name": "types",
+                  "description": "Filter the types of pinnable items that are returned.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PinnableItemType",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PinnableItemConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedItems",
+              "description": "A list of repositories and gists this profile owner has pinned to their profile",
+              "args": [
+                {
+                  "name": "types",
+                  "description": "Filter the types of pinned items that are returned.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PinnableItemType",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PinnableItemConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pinnedItemsRemaining",
+              "description": "Returns how many more items this profile owner can pin to their profile.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanChangePinnedItems",
+              "description": "Can the viewer pin repositories and gists to the profile?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "websiteUrl",
+              "description": "The public profile website URL.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProfileItemShowcase",
+          "description": "A curatable list of repositories relating to a repository owner, which defaults to showing the most popular repositories they own.",
+          "fields": [
+            {
+              "name": "hasPinnedItems",
+              "description": "Whether or not the owner has pinned any repositories or gists.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "items",
+              "description": "The repositories and gists in the showcase. If the profile owner has any pinned items, those will be returned. Otherwise, the profile owner's popular repositories will be returned.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PinnableItemConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PinnableItemConnection",
+          "description": "The connection type for PinnableItem.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PinnableItemEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "PinnableItem",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PinnableItemEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "PinnableItem",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "PinnableItem",
+          "description": "Types that can be pinned to a profile page.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Gist",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Gist",
+          "description": "A Gist.",
+          "fields": [
+            {
+              "name": "comments",
+              "description": "A list of comments associated with the gist",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "GistCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "The gist description.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "files",
+              "description": "The files in this gist.",
+              "args": [
+                {
+                  "name": "limit",
+                  "description": "The maximum number of files to return.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "10"
+                }
+              ],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "GistFile",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isFork",
+              "description": "Identifies if the gist is a fork.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isPublic",
+              "description": "Whether the gist is public or not.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The gist name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "owner",
+              "description": "The gist owner.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "RepositoryOwner",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pushedAt",
+              "description": "Identifies when the gist was last pushed to.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "stargazers",
+              "description": "A list of users who have starred this starrable.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "StarOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "StargazerConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerHasStarred",
+              "description": "Returns a boolean indicating whether the viewing user has starred this starrable.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Starrable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Starrable",
+          "description": "Things that can be starred.",
+          "fields": [
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "stargazers",
+              "description": "A list of users who have starred this starrable.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "StarOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "StargazerConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerHasStarred",
+              "description": "Returns a boolean indicating whether the viewing user has starred this starrable.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Gist",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Topic",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "StargazerConnection",
+          "description": "The connection type for User.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "StargazerEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "StargazerEdge",
+          "description": "Represents a user that's starred a repository.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "starredAt",
+              "description": "Identifies when the item was starred.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "StarOrder",
+          "description": "Ways in which star connections can be ordered.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field in which to order nodes by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "StarOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The direction in which to order nodes.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "StarOrderField",
+          "description": "Properties by which star connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "STARRED_AT",
+              "description": "Allows ordering a list of stars by when they were created.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GistCommentConnection",
+          "description": "The connection type for GistComment.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "GistCommentEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "GistComment",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GistCommentEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "GistComment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GistComment",
+          "description": "Represents a comment on an Gist.",
+          "fields": [
+            {
+              "name": "author",
+              "description": "The actor who authored the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authorAssociation",
+              "description": "Author's association with the gist.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommentAuthorAssociation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "body",
+              "description": "Identifies the comment body.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "The comment body rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyText",
+              "description": "The body rendered to text.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdViaEmail",
+              "description": "Check if this comment was created via an email reply.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "gist",
+              "description": "The associated gist.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Gist",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "includesCreatedEdit",
+              "description": "Check if this comment was edited and includes an edit with the creation data",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isMinimized",
+              "description": "Returns whether or not a comment has been minimized.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastEditedAt",
+              "description": "The moment the editor made the last edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "minimizedReason",
+              "description": "Returns why the comment was minimized.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies when the comment was published at.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userContentEdits",
+              "description": "A list of edits to this content.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEditConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanDelete",
+              "description": "Check if the current viewer can delete this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanMinimize",
+              "description": "Check if the current viewer can minimize this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCannotUpdateReasons",
+              "description": "Reasons why the current viewer can not update this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "CommentCannotUpdateReason",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerDidAuthor",
+              "description": "Did the viewer author this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Comment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Deletable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Updatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UpdatableComment",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Deletable",
+          "description": "Entities that can be deleted.",
+          "fields": [
+            {
+              "name": "viewerCanDelete",
+              "description": "Check if the current viewer can delete this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CommitComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "GistComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewComment",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GistFile",
+          "description": "A file in a gist.",
+          "fields": [
+            {
+              "name": "encodedName",
+              "description": "The file name encoded to remove characters that are invalid in URL paths.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "encoding",
+              "description": "The gist file encoding.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "extension",
+              "description": "The file extension from the file name.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isImage",
+              "description": "Indicates if this file is an image.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isTruncated",
+              "description": "Whether the file's contents were truncated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "language",
+              "description": "The programming language this file is written in.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Language",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The gist file name.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "size",
+              "description": "The gist file size in bytes.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "text",
+              "description": "UTF8 text data or null if the file is binary",
+              "args": [
+                {
+                  "name": "truncate",
+                  "description": "Optionally truncate the returned file to this length.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Language",
+          "description": "Represents a given language found in repositories.",
+          "fields": [
+            {
+              "name": "color",
+              "description": "The color defined for the current language.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the current language.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "PinnableItemType",
+          "description": "Represents items that can be pinned to a profile page or dashboard.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "REPOSITORY",
+              "description": "A repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "GIST",
+              "description": "A gist.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ISSUE",
+              "description": "An issue.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProjectConnection",
+          "description": "A list of projects associated with the owner.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ProjectEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Project",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ProjectEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ProjectOrder",
+          "description": "Ways in which lists of projects can be ordered upon return.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field in which to order projects by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ProjectOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The direction in which to order projects by the specified field.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ProjectOrderField",
+          "description": "Properties by which project connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Order projects by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UPDATED_AT",
+              "description": "Order projects by update time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NAME",
+              "description": "Order projects by name",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Bot",
+          "description": "A special type of user which takes actions on behalf of GitHub Apps.",
+          "fields": [
+            {
+              "name": "avatarUrl",
+              "description": "A URL pointing to the GitHub App's public avatar.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "login",
+              "description": "The username of the actor.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this bot",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this bot",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Actor",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueComment",
+          "description": "Represents a comment on an Issue.",
+          "fields": [
+            {
+              "name": "author",
+              "description": "The actor who authored the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authorAssociation",
+              "description": "Author's association with the subject of the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommentAuthorAssociation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "body",
+              "description": "The body as Markdown.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "The body rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyText",
+              "description": "The body rendered to text.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdViaEmail",
+              "description": "Check if this comment was created via an email reply.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "includesCreatedEdit",
+              "description": "Check if this comment was edited and includes an edit with the creation data",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isMinimized",
+              "description": "Returns whether or not a comment has been minimized.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "Identifies the issue associated with the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Issue",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastEditedAt",
+              "description": "The moment the editor made the last edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "minimizedReason",
+              "description": "Returns why the comment was minimized.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies when the comment was published at.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "Returns the pull request associated with the comment, if this comment was made on a\npull request.\n",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactionGroups",
+              "description": "A list of reactions grouped by content left on the subject.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "ReactionGroup",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactions",
+              "description": "A list of Reactions left on the Issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "content",
+                  "description": "Allows filtering Reactions by emoji.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "ReactionContent",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Allows specifying the order in which reactions are returned.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ReactionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this issue comment",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this issue comment",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userContentEdits",
+              "description": "A list of edits to this content.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEditConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanDelete",
+              "description": "Check if the current viewer can delete this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanMinimize",
+              "description": "Check if the current viewer can minimize this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanReact",
+              "description": "Can user react to this subject",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCannotUpdateReasons",
+              "description": "Reasons why the current viewer can not update this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "CommentCannotUpdateReason",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerDidAuthor",
+              "description": "Did the viewer author this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Comment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Deletable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Updatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UpdatableComment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Reactable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryNode",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Reactable",
+          "description": "Represents a subject that can be reacted on.",
+          "fields": [
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactionGroups",
+              "description": "A list of reactions grouped by content left on the subject.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "ReactionGroup",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactions",
+              "description": "A list of Reactions left on the Issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "content",
+                  "description": "Allows filtering Reactions by emoji.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "ReactionContent",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Allows specifying the order in which reactions are returned.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ReactionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanReact",
+              "description": "Can user react to this subject",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CommitComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewComment",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReactionGroup",
+          "description": "A group of emoji reactions to a particular piece of content.",
+          "fields": [
+            {
+              "name": "content",
+              "description": "Identifies the emoji reaction.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ReactionContent",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies when the reaction was created.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "The subject that was reacted to.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Reactable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "users",
+              "description": "Users who have reacted to the reaction subject with the emotion represented by this reaction group",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactingUserConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerHasReacted",
+              "description": "Whether or not the authenticated user has left a reaction on the subject.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ReactionContent",
+          "description": "Emojis that can be attached to Issues, Pull Requests and Comments.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "THUMBS_UP",
+              "description": "Represents the 👍 emoji.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "THUMBS_DOWN",
+              "description": "Represents the 👎 emoji.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "LAUGH",
+              "description": "Represents the 😄 emoji.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "HOORAY",
+              "description": "Represents the 🎉 emoji.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CONFUSED",
+              "description": "Represents the 😕 emoji.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "HEART",
+              "description": "Represents the ❤️ emoji.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ROCKET",
+              "description": "Represents the 🚀 emoji.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "EYES",
+              "description": "Represents the 👀 emoji.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReactingUserConnection",
+          "description": "The connection type for User.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactingUserEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReactingUserEdge",
+          "description": "Represents a user that's made a reaction.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactedAt",
+              "description": "The moment when the user made the reaction.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReactionConnection",
+          "description": "A list of reactions that have been left on the subject.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactionEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Reaction",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerHasReacted",
+              "description": "Whether or not the authenticated user has left a reaction on the subject.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReactionEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Reaction",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Reaction",
+          "description": "An emoji reaction to a particular piece of content.",
+          "fields": [
+            {
+              "name": "content",
+              "description": "Identifies the emoji reaction.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ReactionContent",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactable",
+              "description": "The reactable piece of content",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Reactable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "Identifies the user who created this reaction.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ReactionOrder",
+          "description": "Ways in which lists of reactions can be ordered upon return.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field in which to order reactions by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ReactionOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The direction in which to order reactions by the specified field.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ReactionOrderField",
+          "description": "A list of fields that reactions can be ordered by.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Allows ordering a list of reactions by when they were created.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "RepositoryInfo",
+          "description": "A subset of repository info.",
+          "fields": [
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "The description of the repository.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "descriptionHTML",
+              "description": "The description of the repository rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "forkCount",
+              "description": "Returns how many forks there are of this repository in the whole network.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasIssuesEnabled",
+              "description": "Indicates if the repository has issues feature enabled.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasWikiEnabled",
+              "description": "Indicates if the repository has wiki feature enabled.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "homepageUrl",
+              "description": "The repository's URL.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isArchived",
+              "description": "Indicates if the repository is unmaintained.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isFork",
+              "description": "Identifies if the repository is a fork.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isLocked",
+              "description": "Indicates if the repository has been locked or not.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isMirror",
+              "description": "Identifies if the repository is a mirror.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isPrivate",
+              "description": "Identifies if the repository is private.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "licenseInfo",
+              "description": "The license associated with the repository",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "License",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lockReason",
+              "description": "The reason the repository has been locked.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "RepositoryLockReason",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mirrorUrl",
+              "description": "The repository's original mirror URL.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nameWithOwner",
+              "description": "The repository's name with owner.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "owner",
+              "description": "The User owner of the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "RepositoryOwner",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pushedAt",
+              "description": "Identifies when the repository was last pushed to.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this repository",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "shortDescriptionHTML",
+              "description": "A description of the repository, rendered to HTML without any links in it.",
+              "args": [
+                {
+                  "name": "limit",
+                  "description": "How many characters to return.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "200"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this repository",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "ENUM",
+          "name": "RepositoryLockReason",
+          "description": "The possible reasons a given repository could be in a locked state.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "MOVING",
+              "description": "The repository is locked due to a move.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "BILLING",
+              "description": "The repository is locked due to a billing related reason.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "RENAME",
+              "description": "The repository is locked due to a rename.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MIGRATING",
+              "description": "The repository is locked due to a migration.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "License",
+          "description": "A repository's open source license",
+          "fields": [
+            {
+              "name": "body",
+              "description": "The full text of the license",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "conditions",
+              "description": "The conditions set by the license",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "LicenseRule",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "A human-readable description of the license",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "featured",
+              "description": "Whether the license should be featured",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hidden",
+              "description": "Whether the license should be displayed in license pickers",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "implementation",
+              "description": "Instructions on how to implement the license",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "key",
+              "description": "The lowercased SPDX ID of the license",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "limitations",
+              "description": "The limitations set by the license",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "LicenseRule",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The license full name specified by <https://spdx.org/licenses>",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nickname",
+              "description": "Customary short name if applicable (e.g, GPLv3)",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "permissions",
+              "description": "The permissions set by the license",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "LicenseRule",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pseudoLicense",
+              "description": "Whether the license is a pseudo-license placeholder (e.g., other, no-license)",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "spdxId",
+              "description": "Short identifier specified by <https://spdx.org/licenses>",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "URL to the license on <https://choosealicense.com>",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "LicenseRule",
+          "description": "Describes a License's conditions, permissions, and limitations",
+          "fields": [
+            {
+              "name": "description",
+              "description": "A description of the rule",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "key",
+              "description": "The machine-readable rule key",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "label",
+              "description": "The human-readable rule label",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryTopicConnection",
+          "description": "The connection type for RepositoryTopic.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryTopicEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryTopic",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryTopicEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RepositoryTopic",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryTopic",
+          "description": "A repository-topic connects a repository to a topic.",
+          "fields": [
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this repository-topic.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "topic",
+              "description": "The topic.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Topic",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this repository-topic.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Topic",
+          "description": "A topic aggregates entities that are related to a subject.",
+          "fields": [
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The topic's name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "relatedTopics",
+              "description": "A list of related topics, including aliases of this topic, sorted with the most relevant\nfirst. Returns up to 10 Topics.\n",
+              "args": [
+                {
+                  "name": "first",
+                  "description": "How many topics to return.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "3"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "Topic",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "stargazers",
+              "description": "A list of users who have starred this starrable.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "StarOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "StargazerConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerHasStarred",
+              "description": "Returns a boolean indicating whether the viewing user has starred this starrable.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Starrable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Release",
+          "description": "A release contains the content for a release.",
+          "fields": [
+            {
+              "name": "author",
+              "description": "The author of the release",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "Identifies the description of the release.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isDraft",
+              "description": "Whether or not the release is a draft",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isPrerelease",
+              "description": "Whether or not the release is a prerelease",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "Identifies the title of the release.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies the date and time when the release was created.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "releaseAssets",
+              "description": "List of releases assets which are dependent on this release.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "name",
+                  "description": "A list of names to filter the assets by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReleaseAssetConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this issue",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "tag",
+              "description": "The Git tag the release points to",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "tagName",
+              "description": "The name of the release's Git tag",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this issue",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Ref",
+          "description": "Represents a Git reference.",
+          "fields": [
+            {
+              "name": "associatedPullRequests",
+              "description": "A list of pull requests with this ref as the head ref.",
+              "args": [
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PullRequestState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "headRefName",
+                  "description": "The head ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "baseRefName",
+                  "description": "The base ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for pull requests returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The ref name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "prefix",
+              "description": "The ref's prefix, such as `refs/heads/` or `refs/tags/`.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository the ref belongs to.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "target",
+              "description": "The object the ref points to.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "GitObject",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "GitObject",
+          "description": "Represents a Git object.",
+          "fields": [
+            {
+              "name": "abbreviatedOid",
+              "description": "An abbreviated version of the Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitResourcePath",
+              "description": "The HTTP path for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitUrl",
+              "description": "The HTTP URL for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "oid",
+              "description": "The Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The Repository the Git object belongs to",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Blob",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Commit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Tag",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Tree",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "SCALAR",
+          "name": "GitObjectID",
+          "description": "A Git object ID.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "RepositoryNode",
+          "description": "Represents a object that belongs to a repository.",
+          "fields": [
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CommitComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CommitCommentThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestCommitCommentThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewComment",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Blob",
+          "description": "Represents a Git blob.",
+          "fields": [
+            {
+              "name": "abbreviatedOid",
+              "description": "An abbreviated version of the Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "byteSize",
+              "description": "Byte size of Blob object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitResourcePath",
+              "description": "The HTTP path for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitUrl",
+              "description": "The HTTP URL for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isBinary",
+              "description": "Indicates whether the Blob is binary or text",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isTruncated",
+              "description": "Indicates whether the contents is truncated",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "oid",
+              "description": "The Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The Repository the Git object belongs to",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "text",
+              "description": "UTF8 text data or null if the Blob is binary",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "GitObject",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Commit",
+          "description": "Represents a Git commit.",
+          "fields": [
+            {
+              "name": "abbreviatedOid",
+              "description": "An abbreviated version of the Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "additions",
+              "description": "The number of additions in this commit.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "associatedPullRequests",
+              "description": "The pull requests associated with a commit",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for pull requests.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "PullRequestOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"CREATED_AT\",direction:\"ASC\"}"
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "author",
+              "description": "Authorship details of the commit.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "GitActor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authoredByCommitter",
+              "description": "Check if the committer and the author match.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authoredDate",
+              "description": "The datetime when this commit was authored.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "blame",
+              "description": "Fetches `git blame` information.",
+              "args": [
+                {
+                  "name": "path",
+                  "description": "The file whose Git blame information you want.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Blame",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "changedFiles",
+              "description": "The number of changed files in this commit.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "comments",
+              "description": "Comments made on the commit.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitResourcePath",
+              "description": "The HTTP path for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitUrl",
+              "description": "The HTTP URL for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "committedDate",
+              "description": "The datetime when this commit was committed.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "committedViaWeb",
+              "description": "Check if commited via GitHub web UI.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "committer",
+              "description": "Committership details of the commit.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "GitActor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletions",
+              "description": "The number of deletions in this commit.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deployments",
+              "description": "The deployments associated with a commit.",
+              "args": [
+                {
+                  "name": "environments",
+                  "description": "Environments to list deployments for",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for deployments returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "DeploymentOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"CREATED_AT\",direction:\"ASC\"}"
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeploymentConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "history",
+              "description": "The linear commit history starting from (and including) this commit, in the same order as `git log`.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "path",
+                  "description": "If non-null, filters history to only show commits touching files under this path.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "author",
+                  "description": "If non-null, filters history to only show commits with matching authorship.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "CommitAuthor",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "since",
+                  "description": "Allows specifying a beginning time or date for fetching commits.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "GitTimestamp",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "until",
+                  "description": "Allows specifying an ending time or date for fetching commits.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "GitTimestamp",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitHistoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "message",
+              "description": "The Git commit message",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "messageBody",
+              "description": "The Git commit message body",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "messageBodyHTML",
+              "description": "The commit message body rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "messageHeadline",
+              "description": "The Git commit message headline",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "messageHeadlineHTML",
+              "description": "The commit message headline rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "oid",
+              "description": "The Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "parents",
+              "description": "The parents of a commit.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pushedDate",
+              "description": "The datetime when this commit was pushed.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The Repository this commit belongs to",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this commit",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signature",
+              "description": "Commit signing information, if present.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "GitSignature",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "status",
+              "description": "Status information for this commit",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Status",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "tarballUrl",
+              "description": "Returns a URL to download a tarball archive for a repository.\nNote: For private repositories, these links are temporary and expire after five minutes.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "tree",
+              "description": "Commit's root Tree",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Tree",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "treeResourcePath",
+              "description": "The HTTP path for the tree of this commit",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "treeUrl",
+              "description": "The HTTP URL for the tree of this commit",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this commit",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanSubscribe",
+              "description": "Check if the viewer is able to change their subscription status for the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerSubscription",
+              "description": "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "SubscriptionState",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "zipballUrl",
+              "description": "Returns a URL to download a zipball archive for a repository.\nNote: For private repositories, these links are temporary and expire after five minutes.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "GitObject",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Subscribable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Subscribable",
+          "description": "Entities that can be subscribed to for web and email notifications.",
+          "fields": [
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanSubscribe",
+              "description": "Check if the viewer is able to change their subscription status for the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerSubscription",
+              "description": "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "SubscriptionState",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Commit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Team",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "ENUM",
+          "name": "SubscriptionState",
+          "description": "The possible states of a subscription.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "UNSUBSCRIBED",
+              "description": "The User is only notified when participating or @mentioned.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SUBSCRIBED",
+              "description": "The User is notified of all conversations.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "IGNORED",
+              "description": "The User is never notified.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Tree",
+          "description": "Represents a Git tree.",
+          "fields": [
+            {
+              "name": "abbreviatedOid",
+              "description": "An abbreviated version of the Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitResourcePath",
+              "description": "The HTTP path for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitUrl",
+              "description": "The HTTP URL for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "entries",
+              "description": "A list of tree entries.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "TreeEntry",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "oid",
+              "description": "The Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The Repository the Git object belongs to",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "GitObject",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TreeEntry",
+          "description": "Represents a Git tree entry.",
+          "fields": [
+            {
+              "name": "mode",
+              "description": "Entry file mode.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "Entry file name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "object",
+              "description": "Entry file object.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "GitObject",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "oid",
+              "description": "Entry file Git object ID.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The Repository the tree entry belongs to",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "type",
+              "description": "Entry file type.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GitActor",
+          "description": "Represents an actor in a Git commit (ie. an author or committer).",
+          "fields": [
+            {
+              "name": "avatarUrl",
+              "description": "A URL pointing to the author's public avatar.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "date",
+              "description": "The timestamp of the Git action (authoring or committing).",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "GitTimestamp",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "email",
+              "description": "The email in the Git commit.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The name in the Git commit.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The GitHub user corresponding to the email field. Null if no such user exists.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "SCALAR",
+          "name": "GitTimestamp",
+          "description": "An ISO-8601 encoded date string. Unlike the DateTime type, GitTimestamp is not converted in UTC.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommitConnection",
+          "description": "The connection type for Commit.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Commit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommitEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommitHistoryConnection",
+          "description": "The connection type for Commit.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Commit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CommitAuthor",
+          "description": "Specifies an author for filtering Git commits.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "id",
+              "description": "ID of a User to filter by. If non-null, only commits authored by this user will be returned. This field takes precedence over emails.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "emails",
+              "description": "Email addresses to filter by. Commits authored by any of the specified email addresses will be returned.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommitCommentConnection",
+          "description": "The connection type for CommitComment.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitCommentEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitComment",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommitCommentEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CommitComment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommitComment",
+          "description": "Represents a comment on a given Commit.",
+          "fields": [
+            {
+              "name": "author",
+              "description": "The actor who authored the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authorAssociation",
+              "description": "Author's association with the subject of the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommentAuthorAssociation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "body",
+              "description": "Identifies the comment body.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "Identifies the comment body rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyText",
+              "description": "The body rendered to text.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commit",
+              "description": "Identifies the commit associated with the comment, if the commit exists.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdViaEmail",
+              "description": "Check if this comment was created via an email reply.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "includesCreatedEdit",
+              "description": "Check if this comment was edited and includes an edit with the creation data",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isMinimized",
+              "description": "Returns whether or not a comment has been minimized.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastEditedAt",
+              "description": "The moment the editor made the last edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "minimizedReason",
+              "description": "Returns why the comment was minimized.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "path",
+              "description": "Identifies the file path associated with the comment.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "position",
+              "description": "Identifies the line position associated with the comment.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies when the comment was published at.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactionGroups",
+              "description": "A list of reactions grouped by content left on the subject.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "ReactionGroup",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactions",
+              "description": "A list of Reactions left on the Issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "content",
+                  "description": "Allows filtering Reactions by emoji.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "ReactionContent",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Allows specifying the order in which reactions are returned.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ReactionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path permalink for this commit comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL permalink for this commit comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userContentEdits",
+              "description": "A list of edits to this content.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEditConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanDelete",
+              "description": "Check if the current viewer can delete this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanMinimize",
+              "description": "Check if the current viewer can minimize this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanReact",
+              "description": "Can user react to this subject",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCannotUpdateReasons",
+              "description": "Reasons why the current viewer can not update this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "CommentCannotUpdateReason",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerDidAuthor",
+              "description": "Did the viewer author this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Comment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Deletable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Updatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UpdatableComment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Reactable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryNode",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "GitSignature",
+          "description": "Information about a signature (GPG or S/MIME) on a Commit or Tag.",
+          "fields": [
+            {
+              "name": "email",
+              "description": "Email used to sign this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isValid",
+              "description": "True if the signature is valid and verified by GitHub.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "payload",
+              "description": "Payload for GPG signing object. Raw ODB object without the signature header.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signature",
+              "description": "ASCII-armored signature header from object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signer",
+              "description": "GitHub user corresponding to the email signing this commit.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "GitSignatureState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "wasSignedByGitHub",
+              "description": "True if the signature was made with GitHub's signing key.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "GpgSignature",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "SmimeSignature",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnknownSignature",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "ENUM",
+          "name": "GitSignatureState",
+          "description": "The state of a Git signature.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "VALID",
+              "description": "Valid signature and verified by GitHub",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INVALID",
+              "description": "Invalid signature",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MALFORMED_SIG",
+              "description": "Malformed signature",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNKNOWN_KEY",
+              "description": "Key used for signing not known to GitHub",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "BAD_EMAIL",
+              "description": "Invalid email used for signing",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNVERIFIED_EMAIL",
+              "description": "Email used for signing unverified on GitHub",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NO_USER",
+              "description": "Email used for signing not known to GitHub",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNKNOWN_SIG_TYPE",
+              "description": "Unknown signature type",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNSIGNED",
+              "description": "Unsigned",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "GPGVERIFY_UNAVAILABLE",
+              "description": "Internal error - the GPG verification service is unavailable at the moment",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "GPGVERIFY_ERROR",
+              "description": "Internal error - the GPG verification service misbehaved",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NOT_SIGNING_KEY",
+              "description": "The usage flags for the key that signed this don't allow signing",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "EXPIRED_KEY",
+              "description": "Signing key expired",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OCSP_PENDING",
+              "description": "Valid signature, pending certificate revocation checking",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OCSP_ERROR",
+              "description": "Valid siganture, though certificate revocation check failed",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "BAD_CERT",
+              "description": "The signing certificate or its chain could not be verified",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OCSP_REVOKED",
+              "description": "One or more certificates in chain has been revoked",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Status",
+          "description": "Represents a commit status.",
+          "fields": [
+            {
+              "name": "commit",
+              "description": "The commit this status is attached to.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "context",
+              "description": "Looks up an individual status context by context name.",
+              "args": [
+                {
+                  "name": "name",
+                  "description": "The context name.",
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "StatusContext",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "contexts",
+              "description": "The individual status contexts for this commit.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "StatusContext",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "The combined commit status.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "StatusState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "StatusState",
+          "description": "The possible commit status states.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "EXPECTED",
+              "description": "Status is expected.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ERROR",
+              "description": "Status is errored.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FAILURE",
+              "description": "Status is failing.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PENDING",
+              "description": "Status is pending.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SUCCESS",
+              "description": "Status is successful.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "StatusContext",
+          "description": "Represents an individual commit status context",
+          "fields": [
+            {
+              "name": "commit",
+              "description": "This commit this status context is attached to.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "context",
+              "description": "The name of this status context.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "creator",
+              "description": "The actor who created this status context.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "The description for this status context.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "The state of this status context.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "StatusState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "targetUrl",
+              "description": "The URL for this status context.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "PullRequestState",
+          "description": "The possible states of a pull request.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OPEN",
+              "description": "A pull request that is still open.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CLOSED",
+              "description": "A pull request that has been closed without being merged.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MERGED",
+              "description": "A pull request that has been closed by being merged.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Blame",
+          "description": "Represents a Git blame.",
+          "fields": [
+            {
+              "name": "ranges",
+              "description": "The list of ranges from a Git blame.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "BlameRange",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BlameRange",
+          "description": "Represents a range of information from a Git blame.",
+          "fields": [
+            {
+              "name": "age",
+              "description": "Identifies the recency of the change, from 1 (new) to 10 (old). This is calculated as a 2-quantile and determines the length of distance between the median age of all the changes in the file and the recency of the current range's change.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commit",
+              "description": "Identifies the line author",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Commit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "endingLine",
+              "description": "The ending line for the range",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "startingLine",
+              "description": "The starting line for the range",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeploymentConnection",
+          "description": "The connection type for Deployment.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "DeploymentEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Deployment",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeploymentEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Deployment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Deployment",
+          "description": "Represents triggered deployment instance.",
+          "fields": [
+            {
+              "name": "commit",
+              "description": "Identifies the commit sha of the deployment.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitOid",
+              "description": "Identifies the oid of the deployment commit, even if the commit has been deleted.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "creator",
+              "description": "Identifies the actor who triggered the deployment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "The deployment description.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "environment",
+              "description": "The environment to which this deployment was made.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "latestStatus",
+              "description": "The latest status of this deployment.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeploymentStatus",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "payload",
+              "description": "Extra information that a deployment system might need.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ref",
+              "description": "Identifies the Ref of the deployment, if the deployment was created by ref.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "Identifies the repository associated with the deployment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "The current state of the deployment.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "DeploymentState",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "statuses",
+              "description": "A list of statuses associated with the deployment.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeploymentStatusConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "task",
+              "description": "The deployment task.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeploymentStatusConnection",
+          "description": "The connection type for DeploymentStatus.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "DeploymentStatusEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "DeploymentStatus",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeploymentStatusEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeploymentStatus",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeploymentStatus",
+          "description": "Describes the status of a given deployment attempt.",
+          "fields": [
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "creator",
+              "description": "Identifies the actor who triggered the deployment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deployment",
+              "description": "Identifies the deployment associated with status.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Deployment",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "Identifies the description of the deployment.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "environmentUrl",
+              "description": "Identifies the environment URL of the deployment.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "logUrl",
+              "description": "Identifies the log URL of the deployment.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "Identifies the current state of the deployment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "DeploymentStatusState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "DeploymentStatusState",
+          "description": "The possible states for a deployment status.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "PENDING",
+              "description": "The deployment is pending.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SUCCESS",
+              "description": "The deployment was successful.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FAILURE",
+              "description": "The deployment has failed.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INACTIVE",
+              "description": "The deployment is inactive.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ERROR",
+              "description": "The deployment experienced an error.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "QUEUED",
+              "description": "The deployment is queued",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "IN_PROGRESS",
+              "description": "The deployment is in progress.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "DeploymentState",
+          "description": "The possible states in which a deployment can be.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ABANDONED",
+              "description": "The pending deployment was not updated after 30 minutes.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ACTIVE",
+              "description": "The deployment is currently active.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DESTROYED",
+              "description": "An inactive transient deployment.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ERROR",
+              "description": "The deployment experienced an error.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FAILURE",
+              "description": "The deployment has failed.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INACTIVE",
+              "description": "The deployment is inactive.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PENDING",
+              "description": "The deployment is pending.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "QUEUED",
+              "description": "The deployment has queued",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "IN_PROGRESS",
+              "description": "The deployment is in progress.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeploymentOrder",
+          "description": "Ordering options for deployment connections",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order deployments by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "DeploymentOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "DeploymentOrderField",
+          "description": "Properties by which deployment connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Order collection by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "PullRequestOrder",
+          "description": "Ways in which lists of issues can be ordered upon return.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field in which to order pull requests by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "PullRequestOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The direction in which to order pull requests by the specified field.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "PullRequestOrderField",
+          "description": "Properties by which pull_requests connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Order pull_requests by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UPDATED_AT",
+              "description": "Order pull_requests by update time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReleaseAssetConnection",
+          "description": "The connection type for ReleaseAsset.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReleaseAssetEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReleaseAsset",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReleaseAssetEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ReleaseAsset",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReleaseAsset",
+          "description": "A release asset contains the content for a release asset.",
+          "fields": [
+            {
+              "name": "contentType",
+              "description": "The asset's content-type",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "downloadCount",
+              "description": "The number of times this asset was downloaded",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "downloadUrl",
+              "description": "Identifies the URL where you can download the release asset via the browser.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "Identifies the title of the release asset.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "release",
+              "description": "Release that the asset is associated with",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Release",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "size",
+              "description": "The size (in bytes) of the asset",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "uploadedBy",
+              "description": "The user that performed the upload",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "Identifies the URL of the release asset.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MarketplaceCategory",
+          "description": "A public description of a Marketplace category.",
+          "fields": [
+            {
+              "name": "description",
+              "description": "The category's description.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "howItWorks",
+              "description": "The technical description of how apps listed in this category work with GitHub.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The category's name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "primaryListingCount",
+              "description": "How many Marketplace listings have this as their primary category.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this Marketplace category.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "secondaryListingCount",
+              "description": "How many Marketplace listings have this as their secondary category.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "slug",
+              "description": "The short name of the category used in its URL.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this Marketplace category.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MarketplaceListingConnection",
+          "description": "Look up Marketplace Listings",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "MarketplaceListingEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "MarketplaceListing",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MarketplaceListingEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "MarketplaceListing",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReleaseConnection",
+          "description": "The connection type for Release.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReleaseEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Release",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReleaseEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Release",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ReleaseOrder",
+          "description": "Ways in which lists of releases can be ordered upon return.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field in which to order releases by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ReleaseOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The direction in which to order releases by the specified field.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ReleaseOrderField",
+          "description": "Properties by which release connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Order releases by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NAME",
+              "description": "Order releases alphabetically by name",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "IssuePubSubTopic",
+          "description": "The possible PubSub channels for an issue.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "UPDATED",
+              "description": "The channel ID for observing issue updates.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MARKASREAD",
+              "description": "The channel ID for marking an issue as read.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "TIMELINE",
+              "description": "The channel ID for updating items on the issue timeline.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "STATE",
+              "description": "The channel ID for observing issue state updates.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "OrganizationConnection",
+          "description": "The connection type for Organization.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "OrganizationEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Organization",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "OrganizationEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Organization",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "OrganizationInvitation",
+          "description": "An Invitation for a user to an organization.",
+          "fields": [
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "email",
+              "description": "The email address of the user invited to the organization.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "invitationType",
+              "description": "The type of invitation that was sent (e.g. email, user).",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrganizationInvitationType",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "invitee",
+              "description": "The user who was invited to the organization.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "inviter",
+              "description": "The user who created the invitation.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organization",
+              "description": "The organization the invite is for",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Organization",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "role",
+              "description": "The user's pending role in the organization (e.g. member, owner).",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrganizationInvitationRole",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "OrganizationInvitationType",
+          "description": "The possible organization invitation types.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "USER",
+              "description": "The invitation was to an existing user.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "EMAIL",
+              "description": "The invitation was to an email address.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "OrganizationInvitationRole",
+          "description": "The possible organization invitation roles.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "DIRECT_MEMBER",
+              "description": "The user is invited to be a direct member of the organization.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ADMIN",
+              "description": "The user is invited to be an admin of the organization.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "BILLING_MANAGER",
+              "description": "The user is invited to be a billing manager of the organization.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REINSTATE",
+              "description": "The user's previous role will be reinstated.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TeamConnection",
+          "description": "The connection type for Team.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Team",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TeamEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Team",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Team",
+          "description": "A team of users in an organization.",
+          "fields": [
+            {
+              "name": "ancestors",
+              "description": "A list of teams that are ancestors of this team.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "avatarUrl",
+              "description": "A URL pointing to the team's avatar.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size in pixels of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "400"
+                }
+              ],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "childTeams",
+              "description": "List of child teams belonging to this team",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Order for connection",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "TeamOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "userLogins",
+                  "description": "User logins to filter by",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "immediateOnly",
+                  "description": "Whether to list immediate child teams or all descendant child teams.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "true"
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "combinedSlug",
+              "description": "The slug corresponding to the organization and team.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "The description of the team.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editTeamResourcePath",
+              "description": "The HTTP path for editing this team",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editTeamUrl",
+              "description": "The HTTP URL for editing this team",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "invitations",
+              "description": "A list of pending invitations for users to this team",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "OrganizationInvitationConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "memberStatuses",
+              "description": "Get the status messages members of this entity have set that are either public or visible only to the organization.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for user statuses returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "UserStatusOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"UPDATED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserStatusConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "members",
+              "description": "A list of users who are members of this team.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "query",
+                  "description": "The search string to look for.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "membership",
+                  "description": "Filter by membership type",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "TeamMembershipType",
+                    "ofType": null
+                  },
+                  "defaultValue": "ALL"
+                },
+                {
+                  "name": "role",
+                  "description": "Filter by team member role",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "TeamMemberRole",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "TeamMemberOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamMemberConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "membersResourcePath",
+              "description": "The HTTP path for the team' members",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "membersUrl",
+              "description": "The HTTP URL for the team' members",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the team.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "newTeamResourcePath",
+              "description": "The HTTP path creating a new team",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "newTeamUrl",
+              "description": "The HTTP URL creating a new team",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organization",
+              "description": "The organization that owns this team.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Organization",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "parentTeam",
+              "description": "The parent team of the team.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Team",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "privacy",
+              "description": "The level of privacy the team has.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "TeamPrivacy",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositories",
+              "description": "A list of repositories this team has access to.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "query",
+                  "description": "The search string to look for.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Order for the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "TeamRepositoryOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamRepositoryConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositoriesResourcePath",
+              "description": "The HTTP path for this team's repositories",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositoriesUrl",
+              "description": "The HTTP URL for this team's repositories",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this team",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "slug",
+              "description": "The slug corresponding to the team.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "teamsResourcePath",
+              "description": "The HTTP path for this team's teams",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "teamsUrl",
+              "description": "The HTTP URL for this team's teams",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this team",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanAdminister",
+              "description": "Team is adminable by the viewer.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanSubscribe",
+              "description": "Check if the viewer is able to change their subscription status for the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerSubscription",
+              "description": "Identifies if the viewer is watching, not watching, or ignoring the subscribable entity.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "SubscriptionState",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Subscribable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "MemberStatusable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "TeamPrivacy",
+          "description": "The possible team privacy values.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "SECRET",
+              "description": "A secret team can only be seen by its members.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "VISIBLE",
+              "description": "A visible team can be seen and @mentioned by every member of the organization.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TeamMemberConnection",
+          "description": "The connection type for User.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamMemberEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TeamMemberEdge",
+          "description": "Represents a user who is a member of a team.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "memberAccessResourcePath",
+              "description": "The HTTP path to the organization's member access page.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "memberAccessUrl",
+              "description": "The HTTP URL to the organization's member access page.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "role",
+              "description": "The role the member has on the team.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "TeamMemberRole",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "TeamMemberRole",
+          "description": "The possible team member roles; either 'maintainer' or 'member'.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "MAINTAINER",
+              "description": "A team maintainer has permission to add and remove team members.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MEMBER",
+              "description": "A team member has no administrative permissions on the team.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "TeamMembershipType",
+          "description": "Defines which types of team members are included in the returned list. Can be one of IMMEDIATE, CHILD_TEAM or ALL.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "IMMEDIATE",
+              "description": "Includes only immediate members of the team.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CHILD_TEAM",
+              "description": "Includes only child team members for the team.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ALL",
+              "description": "Includes immediate and child team members for the team.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "TeamMemberOrder",
+          "description": "Ordering options for team member connections",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order team members by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "TeamMemberOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "TeamMemberOrderField",
+          "description": "Properties by which team member connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "LOGIN",
+              "description": "Order team members by login",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CREATED_AT",
+              "description": "Order team members by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TeamRepositoryConnection",
+          "description": "The connection type for Repository.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamRepositoryEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TeamRepositoryEdge",
+          "description": "Represents a team repository.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "permission",
+              "description": "The permission level the team has on the repository",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "RepositoryPermission",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "RepositoryPermission",
+          "description": "The access level to a repository",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ADMIN",
+              "description": "Can read, clone, push, and add collaborators",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "WRITE",
+              "description": "Can read, clone and push",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "READ",
+              "description": "Can read and clone",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "TeamRepositoryOrder",
+          "description": "Ordering options for team repository connections",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order repositories by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "TeamRepositoryOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "TeamRepositoryOrderField",
+          "description": "Properties by which team repository connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Order repositories by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UPDATED_AT",
+              "description": "Order repositories by update time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PUSHED_AT",
+              "description": "Order repositories by push time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NAME",
+              "description": "Order repositories by name",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PERMISSION",
+              "description": "Order repositories by permission",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "STARGAZERS",
+              "description": "Order repositories by number of stargazers",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "OrganizationInvitationConnection",
+          "description": "The connection type for OrganizationInvitation.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "OrganizationInvitationEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "OrganizationInvitation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "OrganizationInvitationEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "OrganizationInvitation",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "TeamOrder",
+          "description": "Ways in which team connections can be ordered.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field in which to order nodes by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "TeamOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The direction in which to order nodes.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "TeamOrderField",
+          "description": "Properties by which team connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "NAME",
+              "description": "Allows ordering a list of teams by name.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "DefaultRepositoryPermissionField",
+          "description": "The possible default permissions for repositories.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "NONE",
+              "description": "No access",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "READ",
+              "description": "Can read repos by default",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "WRITE",
+              "description": "Can read and write repos by default",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ADMIN",
+              "description": "Can read, write, and administrate repos by default",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ExternalIdentityConnection",
+          "description": "The connection type for ExternalIdentity.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ExternalIdentityEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ExternalIdentity",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ExternalIdentityEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ExternalIdentity",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ExternalIdentity",
+          "description": "An external identity provisioned by SAML SSO or SCIM.",
+          "fields": [
+            {
+              "name": "guid",
+              "description": "The GUID for this identity",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organizationInvitation",
+              "description": "Organization invitation for this SCIM-provisioned external identity",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "OrganizationInvitation",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "samlIdentity",
+              "description": "SAML Identity attributes",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ExternalIdentitySamlAttributes",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "scimIdentity",
+              "description": "SCIM Identity attributes",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ExternalIdentityScimAttributes",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "User linked to this external identity. Will be NULL if this identity has not been claimed by an organization member.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ExternalIdentitySamlAttributes",
+          "description": "SAML attributes for the External Identity",
+          "fields": [
+            {
+              "name": "nameId",
+              "description": "The NameID of the SAML identity",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ExternalIdentityScimAttributes",
+          "description": "SCIM attributes for the External Identity",
+          "fields": [
+            {
+              "name": "username",
+              "description": "The userName of the SCIM identity",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PublicKey",
+          "description": "A user's public key.",
+          "fields": [
+            {
+              "name": "accessedAt",
+              "description": "The last time this authorization was used to perform an action",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "fingerprint",
+              "description": "The fingerprint for this PublicKey",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isReadOnly",
+              "description": "Whether this PublicKey is read-only or not",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "key",
+              "description": "The public key string",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "SCALAR",
+          "name": "X509Certificate",
+          "description": "A valid x509 certificate string",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "IdentityProviderConfigurationState",
+          "description": "The possible states in which authentication can be configured with an identity provider.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ENFORCED",
+              "description": "Authentication with an identity provider is configured and enforced.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CONFIGURED",
+              "description": "Authentication with an identity provider is configured but not enforced.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNCONFIGURED",
+              "description": "Authentication with an identity provider is not configured.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "SCALAR",
+          "name": "Date",
+          "description": "An ISO-8601 encoded date string.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "OrganizationIdentityProvider",
+          "description": "An Identity Provider configured to provision SAML and SCIM identities for Organizations",
+          "fields": [
+            {
+              "name": "digestMethod",
+              "description": "The digest algorithm used to sign SAML requests for the Identity Provider.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "externalIdentities",
+              "description": "External Identities provisioned by this Identity Provider",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ExternalIdentityConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "idpCertificate",
+              "description": "The x509 certificate used by the Identity Provder to sign assertions and responses.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "X509Certificate",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issuer",
+              "description": "The Issuer Entity ID for the SAML Identity Provider",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "organization",
+              "description": "Organization this Identity Provider belongs to",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Organization",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signatureMethod",
+              "description": "The signature algorithm used to sign SAML requests for the Identity Provider.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ssoUrl",
+              "description": "The URL endpoint for the Identity Provider's SAML SSO.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "OrganizationMemberConnection",
+          "description": "The connection type for User.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "OrganizationMemberEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "OrganizationMemberEdge",
+          "description": "Represents a user within an organization.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasTwoFactorEnabled",
+              "description": "Whether the organization member has two factor enabled or not. Returns null if information is not available to viewer.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "role",
+              "description": "The role this user has in the organization.",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "OrganizationMemberRole",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "OrganizationMemberRole",
+          "description": "The possible roles within an organization for its members.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "MEMBER",
+              "description": "The user is a member of the organization.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ADMIN",
+              "description": "The user is an administrator of the organization.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "TeamRole",
+          "description": "The role of a user on a team.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ADMIN",
+              "description": "User has admin rights on the team.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MEMBER",
+              "description": "User is a member of the team.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GistConnection",
+          "description": "The connection type for Gist.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "GistEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Gist",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GistEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Gist",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "GistPrivacy",
+          "description": "The privacy of a Gist",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "PUBLIC",
+              "description": "Public",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SECRET",
+              "description": "Secret",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ALL",
+              "description": "Gists that are public and secret",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "GistOrder",
+          "description": "Ordering options for gist connections",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order repositories by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "GistOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "GistOrderField",
+          "description": "Properties by which gist connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CREATED_AT",
+              "description": "Order gists by creation time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UPDATED_AT",
+              "description": "Order gists by update time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PUSHED_AT",
+              "description": "Order gists by push time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryInvitationEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RepositoryInvitation",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryInvitation",
+          "description": "An invitation for a user to be added to a repository.",
+          "fields": [
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "invitee",
+              "description": "The user who received the invitation.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "inviter",
+              "description": "The user who created the invitation.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "permission",
+              "description": "The permission granted on this repository by this invitation.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "RepositoryPermission",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The Repository the user is invited to.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "RepositoryInfo",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Mannequin",
+          "description": "A placeholder user for attribution of imported data on GitHub.",
+          "fields": [
+            {
+              "name": "avatarUrl",
+              "description": "A URL pointing to the GitHub App's public avatar.",
+              "args": [
+                {
+                  "name": "size",
+                  "description": "The size of the resulting square image.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "login",
+              "description": "The username of the actor.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTML path to this resource.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The URL to this resource.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Actor",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "LanguageConnection",
+          "description": "A list of languages associated with the parent.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "LanguageEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Language",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalSize",
+              "description": "The total size in bytes of files written in that language.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "LanguageEdge",
+          "description": "Represents the language of a repository.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Language",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "size",
+              "description": "The number of bytes of code written in the language.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Milestone",
+          "description": "Represents a Milestone object on a given repository.",
+          "fields": [
+            {
+              "name": "closed",
+              "description": "`true` if the object is closed (definition of closed may depend on type)",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closedAt",
+              "description": "Identifies the date and time when the object was closed.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "creator",
+              "description": "Identifies the actor who created the milestone.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "Identifies the description of the milestone.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "dueOn",
+              "description": "Identifies the due date of the milestone.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issues",
+              "description": "A list of issues associated with the milestone.",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for issues returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the issues by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "IssueState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "filterBy",
+                  "description": "Filtering options for issues returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueFilters",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "number",
+              "description": "Identifies the number of the milestone.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequests",
+              "description": "A list of pull requests associated with the milestone.",
+              "args": [
+                {
+                  "name": "states",
+                  "description": "A list of states to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "PullRequestState",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "labels",
+                  "description": "A list of label names to filter the pull requests by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "headRefName",
+                  "description": "The head ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "baseRefName",
+                  "description": "The base ref name to filter the pull requests by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for pull requests returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "IssueOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this milestone.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this milestone",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "Identifies the state of the milestone.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "MilestoneState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "title",
+              "description": "Identifies the title of the milestone.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this milestone",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Closable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "MilestoneState",
+          "description": "The possible states of a milestone.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OPEN",
+              "description": "A milestone that is still open.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CLOSED",
+              "description": "A milestone that has been closed.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestChangedFileConnection",
+          "description": "The connection type for PullRequestChangedFile.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestChangedFileEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestChangedFile",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestChangedFileEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestChangedFile",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestChangedFile",
+          "description": "A file changed in a pull request.",
+          "fields": [
+            {
+              "name": "additions",
+              "description": "The number of additions to the file.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletions",
+              "description": "The number of deletions to the file.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "path",
+              "description": "The path of the file.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "MergeableState",
+          "description": "Whether or not a PullRequest can be merged.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "MERGEABLE",
+              "description": "The pull request can be merged.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CONFLICTING",
+              "description": "The pull request cannot be merged due to merge conflicts.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNKNOWN",
+              "description": "The mergeability of the pull request is still being calculated.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewComment",
+          "description": "A review comment associated with a given repository pull request.",
+          "fields": [
+            {
+              "name": "author",
+              "description": "The actor who authored the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authorAssociation",
+              "description": "Author's association with the subject of the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommentAuthorAssociation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "body",
+              "description": "The comment body of this review comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "The comment body of this review comment rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyText",
+              "description": "The comment body of this review comment rendered as plain text.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commit",
+              "description": "Identifies the commit associated with the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Commit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies when the comment was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdViaEmail",
+              "description": "Check if this comment was created via an email reply.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "diffHunk",
+              "description": "The diff hunk to which the comment applies.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "draftedAt",
+              "description": "Identifies when the comment was created in a draft state.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "includesCreatedEdit",
+              "description": "Check if this comment was edited and includes an edit with the creation data",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isMinimized",
+              "description": "Returns whether or not a comment has been minimized.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastEditedAt",
+              "description": "The moment the editor made the last edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "minimizedReason",
+              "description": "Returns why the comment was minimized.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "originalCommit",
+              "description": "Identifies the original commit associated with the comment.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "originalPosition",
+              "description": "The original line index in the diff to which the comment applies.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "outdated",
+              "description": "Identifies when the comment body is outdated",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "path",
+              "description": "The path to which the comment applies.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "position",
+              "description": "The line index in the diff to which the comment applies.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies when the comment was published at.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request associated with this review comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReview",
+              "description": "The pull request review associated with this review comment.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactionGroups",
+              "description": "A list of reactions grouped by content left on the subject.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "ReactionGroup",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactions",
+              "description": "A list of Reactions left on the Issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "content",
+                  "description": "Allows filtering Reactions by emoji.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "ReactionContent",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Allows specifying the order in which reactions are returned.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ReactionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "replyTo",
+              "description": "The comment this is a reply to.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewComment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path permalink for this review comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "Identifies the state of the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "PullRequestReviewCommentState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies when the comment was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL permalink for this review comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userContentEdits",
+              "description": "A list of edits to this content.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEditConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanDelete",
+              "description": "Check if the current viewer can delete this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanMinimize",
+              "description": "Check if the current viewer can minimize this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanReact",
+              "description": "Can user react to this subject",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCannotUpdateReasons",
+              "description": "Reasons why the current viewer can not update this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "CommentCannotUpdateReason",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerDidAuthor",
+              "description": "Did the viewer author this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Comment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Deletable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Updatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UpdatableComment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Reactable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryNode",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReview",
+          "description": "A review object for a given pull request.",
+          "fields": [
+            {
+              "name": "author",
+              "description": "The actor who authored the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "authorAssociation",
+              "description": "Author's association with the subject of the comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommentAuthorAssociation",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "body",
+              "description": "Identifies the pull request review body.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyHTML",
+              "description": "The body of this review rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "bodyText",
+              "description": "The body of this review rendered as plain text.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "comments",
+              "description": "A list of review comments for the current pull request review.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReviewCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commit",
+              "description": "Identifies the commit associated with this pull request review.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdViaEmail",
+              "description": "Check if this comment was created via an email reply.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "editor",
+              "description": "The actor who edited the comment.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "includesCreatedEdit",
+              "description": "Check if this comment was edited and includes an edit with the creation data",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastEditedAt",
+              "description": "The moment the editor made the last edit",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "onBehalfOf",
+              "description": "A list of teams that this review was made on behalf of.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TeamConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "Identifies when the comment was published at.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "Identifies the pull request associated with this pull request review.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactionGroups",
+              "description": "A list of reactions grouped by content left on the subject.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "ReactionGroup",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reactions",
+              "description": "A list of Reactions left on the Issue.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "content",
+                  "description": "Allows filtering Reactions by emoji.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "ReactionContent",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Allows specifying the order in which reactions are returned.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ReactionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReactionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path permalink for this PullRequestReview.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "Identifies the current state of the pull request review.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "PullRequestReviewState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "submittedAt",
+              "description": "Identifies when the Pull Request Review was submitted",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the object was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL permalink for this PullRequestReview.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userContentEdits",
+              "description": "A list of edits to this content.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserContentEditConnection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanDelete",
+              "description": "Check if the current viewer can delete this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanReact",
+              "description": "Can user react to this subject",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUpdate",
+              "description": "Check if the current viewer can update this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCannotUpdateReasons",
+              "description": "Reasons why the current viewer can not update this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "CommentCannotUpdateReason",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerDidAuthor",
+              "description": "Did the viewer author this comment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Comment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Deletable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Updatable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UpdatableComment",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "Reactable",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryNode",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "PullRequestReviewState",
+          "description": "The possible states of a pull request review.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "PENDING",
+              "description": "A review that has not yet been submitted.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "COMMENTED",
+              "description": "An informational review.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "APPROVED",
+              "description": "A review allowing the pull request to merge.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CHANGES_REQUESTED",
+              "description": "A review blocking the pull request from merging.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DISMISSED",
+              "description": "A review that has been dismissed.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewCommentConnection",
+          "description": "The connection type for PullRequestReviewComment.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReviewCommentEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReviewComment",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewCommentEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewComment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewThread",
+          "description": "A threaded list of comments for a given pull request.",
+          "fields": [
+            {
+              "name": "comments",
+              "description": "A list of pull request comments associated with the thread.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReviewCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isResolved",
+              "description": "Whether this thread has been resolved",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "Identifies the pull request associated with this thread.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "Identifies the repository associated with this thread.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resolvedBy",
+              "description": "The user who resolved this thread",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanResolve",
+              "description": "Whether or not the viewer can resolve this thread",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "viewerCanUnresolve",
+              "description": "Whether or not the viewer can unresolve this thread",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestCommit",
+          "description": "Represents a Git commit part of a pull request.",
+          "fields": [
+            {
+              "name": "commit",
+              "description": "The Git commit object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Commit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request this commit belongs to",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this pull request commit",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this pull request commit",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewThreadConnection",
+          "description": "Review comment threads for a pull request review.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReviewThreadEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReviewThread",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewThreadEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewThread",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "PullRequestReviewCommentState",
+          "description": "The possible states of a pull request review comment.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "PENDING",
+              "description": "A comment that is part of a pending review",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SUBMITTED",
+              "description": "A comment that is part of a submitted review",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "PullRequestPubSubTopic",
+          "description": "The possible PubSub channels for a pull request.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "UPDATED",
+              "description": "The channel ID for observing pull request updates.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MARKASREAD",
+              "description": "The channel ID for marking an pull request as read.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "HEAD_REF",
+              "description": "The channel ID for observing head ref updates.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "TIMELINE",
+              "description": "The channel ID for updating items on the pull request timeline.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "STATE",
+              "description": "The channel ID for observing pull request state updates.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueCommentConnection",
+          "description": "The connection type for IssueComment.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueCommentEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueComment",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueCommentEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "IssueComment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewConnection",
+          "description": "The connection type for PullRequestReview.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReviewEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReview",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestCommitConnection",
+          "description": "The connection type for PullRequestCommit.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestCommitEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestCommit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestCommitEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestCommit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewRequestConnection",
+          "description": "The connection type for ReviewRequest.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReviewRequestEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReviewRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewRequestEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ReviewRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewRequest",
+          "description": "A request for a user to review a pull request.",
+          "fields": [
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "Identifies the pull request associated with this review request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requestedReviewer",
+              "description": "The reviewer that is requested.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "RequestedReviewer",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "RequestedReviewer",
+          "description": "Types that can be requested reviewers.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Team",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Mannequin",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestTimelineConnection",
+          "description": "The connection type for PullRequestTimelineItem.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestTimelineItemEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "PullRequestTimelineItem",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestTimelineItemEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "PullRequestTimelineItem",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "PullRequestTimelineItem",
+          "description": "An item in an pull request timeline",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Commit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CommitCommentThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ClosedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReopenedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "SubscribedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnsubscribedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MergedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CrossReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "AssignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnassignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DemilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RenamedTitleEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DeployedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DeploymentEnvironmentChangedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefDeletedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefRestoredEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefForcePushedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "BaseRefForcePushedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewRequestedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewRequestRemovedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewDismissedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UserBlockedEvent",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommitCommentThread",
+          "description": "A thread of comments on a commit.",
+          "fields": [
+            {
+              "name": "comments",
+              "description": "The comments that exist in this thread.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commit",
+              "description": "The commit the comments were made on.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Commit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "path",
+              "description": "The file the comments were made on.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "position",
+              "description": "The position in the diff for the commit that the comment was made on.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryNode",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ClosedEvent",
+          "description": "Represents a 'closed' event on any `Closable`.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closable",
+              "description": "Object that was closed.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Closable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closer",
+              "description": "Object which triggered the creation of this event.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "Closer",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this closed event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this closed event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "Closer",
+          "description": "The object which triggered a `ClosedEvent`.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Commit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReopenedEvent",
+          "description": "Represents a 'reopened' event on any `Closable`.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closable",
+              "description": "Object that was reopened.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Closable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SubscribedEvent",
+          "description": "Represents a 'subscribed' event on a given `Subscribable`.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subscribable",
+              "description": "Object referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Subscribable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnsubscribedEvent",
+          "description": "Represents an 'unsubscribed' event on a given `Subscribable`.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subscribable",
+              "description": "Object referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Subscribable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MergedEvent",
+          "description": "Represents a 'merged' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commit",
+              "description": "Identifies the commit associated with the `merge` event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mergeRef",
+              "description": "Identifies the Ref associated with the `merge` event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mergeRefName",
+              "description": "Identifies the name of the Ref associated with the `merge` event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this merged event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this merged event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReferencedEvent",
+          "description": "Represents a 'referenced' event on a given `ReferencedSubject`.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commit",
+              "description": "Identifies the commit associated with the 'referenced' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitRepository",
+              "description": "Identifies the repository associated with the 'referenced' event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isCrossRepository",
+              "description": "Reference originated in a different repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isDirectReference",
+              "description": "Checks if the commit message itself references the subject. Can be false in the case of a commit comment reference.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "Object referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "ReferencedSubject",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "ReferencedSubject",
+          "description": "Any referencable object",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CrossReferencedEvent",
+          "description": "Represents a mention made by one issue or pull request to another.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isCrossRepository",
+              "description": "Reference originated in a different repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "referencedAt",
+              "description": "Identifies when the reference was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "source",
+              "description": "Issue or pull request that made the reference.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "ReferencedSubject",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "target",
+              "description": "Issue or pull request to which the reference was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "ReferencedSubject",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "willCloseTarget",
+              "description": "Checks if the target will be closed when the source is merged.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AssignedEvent",
+          "description": "Represents an 'assigned' event on any assignable object.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "assignable",
+              "description": "Identifies the assignable associated with the event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Assignable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "Identifies the user who was assigned.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnassignedEvent",
+          "description": "Represents an 'unassigned' event on any assignable object.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "assignable",
+              "description": "Identifies the assignable associated with the event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Assignable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "Identifies the subject (user) who was unassigned.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "LabeledEvent",
+          "description": "Represents a 'labeled' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "label",
+              "description": "Identifies the label associated with the 'labeled' event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Label",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "labelable",
+              "description": "Identifies the `Labelable` associated with the event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Labelable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnlabeledEvent",
+          "description": "Represents an 'unlabeled' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "label",
+              "description": "Identifies the label associated with the 'unlabeled' event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Label",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "labelable",
+              "description": "Identifies the `Labelable` associated with the event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Labelable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MilestonedEvent",
+          "description": "Represents a 'milestoned' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "milestoneTitle",
+              "description": "Identifies the milestone title associated with the 'milestoned' event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "Object referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "MilestoneItem",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "MilestoneItem",
+          "description": "Types that can be inside a Milestone.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DemilestonedEvent",
+          "description": "Represents a 'demilestoned' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "milestoneTitle",
+              "description": "Identifies the milestone title associated with the 'demilestoned' event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "Object referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "MilestoneItem",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RenamedTitleEvent",
+          "description": "Represents a 'renamed' event on a given issue or pull request",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "currentTitle",
+              "description": "Identifies the current title of the issue or pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "previousTitle",
+              "description": "Identifies the previous title of the issue or pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "Subject that was renamed.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "RenamedTitleSubject",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "RenamedTitleSubject",
+          "description": "An object which has a renamable title",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "LockedEvent",
+          "description": "Represents a 'locked' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lockReason",
+              "description": "Reason that the conversation was locked (optional).",
+              "args": [],
+              "type": {
+                "kind": "ENUM",
+                "name": "LockReason",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lockable",
+              "description": "Object that was locked.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Lockable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnlockedEvent",
+          "description": "Represents an 'unlocked' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lockable",
+              "description": "Object that was unlocked.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "Lockable",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeployedEvent",
+          "description": "Represents a 'deployed' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deployment",
+              "description": "The deployment associated with the 'deployed' event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Deployment",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ref",
+              "description": "The ref associated with the 'deployed' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeploymentEnvironmentChangedEvent",
+          "description": "Represents a 'deployment_environment_changed' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deploymentStatus",
+              "description": "The deployment status that updated the deployment environment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "DeploymentStatus",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "HeadRefDeletedEvent",
+          "description": "Represents a 'head_ref_deleted' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "headRef",
+              "description": "Identifies the Ref associated with the `head_ref_deleted` event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "headRefName",
+              "description": "Identifies the name of the Ref associated with the `head_ref_deleted` event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "HeadRefRestoredEvent",
+          "description": "Represents a 'head_ref_restored' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "HeadRefForcePushedEvent",
+          "description": "Represents a 'head_ref_force_pushed' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "afterCommit",
+              "description": "Identifies the after commit SHA for the 'head_ref_force_pushed' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "beforeCommit",
+              "description": "Identifies the before commit SHA for the 'head_ref_force_pushed' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ref",
+              "description": "Identifies the fully qualified ref name for the 'head_ref_force_pushed' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BaseRefForcePushedEvent",
+          "description": "Represents a 'base_ref_force_pushed' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "afterCommit",
+              "description": "Identifies the after commit SHA for the 'base_ref_force_pushed' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "beforeCommit",
+              "description": "Identifies the before commit SHA for the 'base_ref_force_pushed' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Commit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ref",
+              "description": "Identifies the fully qualified ref name for the 'base_ref_force_pushed' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewRequestedEvent",
+          "description": "Represents an 'review_requested' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requestedReviewer",
+              "description": "Identifies the reviewer whose review was requested.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "RequestedReviewer",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewRequestRemovedEvent",
+          "description": "Represents an 'review_request_removed' event on a given pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requestedReviewer",
+              "description": "Identifies the reviewer whose review request was removed.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "RequestedReviewer",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewDismissedEvent",
+          "description": "Represents a 'review_dismissed' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "dismissalMessage",
+              "description": "Identifies the optional message associated with the 'review_dismissed' event.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "dismissalMessageHTML",
+              "description": "Identifies the optional message associated with the event, rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "message",
+              "description": "Identifies the message associated with the 'review_dismissed' event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "`message` is being removed because it not nullable, whereas the underlying field is optional. Use `dismissalMessage` instead. Removal on 2019-07-01 UTC."
+            },
+            {
+              "name": "messageHtml",
+              "description": "The message associated with the event, rendered to HTML.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "HTML",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "`messageHtml` is being removed because it not nullable, whereas the underlying field is optional. Use `dismissalMessageHTML` instead. Removal on 2019-07-01 UTC."
+            },
+            {
+              "name": "previousReviewState",
+              "description": "Identifies the previous state of the review with the 'review_dismissed' event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "PullRequestReviewState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "PullRequest referenced by event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestCommit",
+              "description": "Identifies the commit which caused the review to become stale.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestCommit",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this review dismissed event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "review",
+              "description": "Identifies the review associated with the 'review_dismissed' event.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this review dismissed event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "UniformResourceLocatable",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UserBlockedEvent",
+          "description": "Represents a 'user_blocked' event on a given user.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "blockDuration",
+              "description": "Number of days that the user was blocked for.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "UserBlockDuration",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "The user who was blocked.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "UserBlockDuration",
+          "description": "The possible durations that a user can be blocked for.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ONE_DAY",
+              "description": "The user was blocked for 1 day",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "THREE_DAYS",
+              "description": "The user was blocked for 3 days",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ONE_WEEK",
+              "description": "The user was blocked for 7 days",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ONE_MONTH",
+              "description": "The user was blocked for 30 days",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PERMANENT",
+              "description": "The user was blocked permanently",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestTimelineItemsConnection",
+          "description": "The connection type for PullRequestTimelineItems.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestTimelineItemsEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "filteredCount",
+              "description": "Identifies the count of items after applying `before` and `after` filters.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "PullRequestTimelineItems",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageCount",
+              "description": "Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the timeline was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestTimelineItemsEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "PullRequestTimelineItems",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "PullRequestTimelineItems",
+          "description": "An item in a pull request timeline",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestCommit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestCommitCommentThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReview",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestReviewThread",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequestRevisionMarker",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "BaseRefChangedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "BaseRefForcePushedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DeployedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DeploymentEnvironmentChangedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefDeletedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefForcePushedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "HeadRefRestoredEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MergedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewDismissedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewRequestedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReviewRequestRemovedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CrossReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "AddedToProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "AssignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ClosedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CommentDeletedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ConvertedNoteToIssueEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DemilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MentionedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MovedColumnsInProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PinnedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RemovedFromProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RenamedTitleEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReopenedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "SubscribedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "TransferredEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnassignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UserBlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnpinnedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnsubscribedEvent",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestCommitCommentThread",
+          "description": "Represents a commit comment thread part of a pull request.",
+          "fields": [
+            {
+              "name": "comments",
+              "description": "The comments that exist in this thread.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CommitCommentConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commit",
+              "description": "The commit the comments were made on.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Commit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "path",
+              "description": "The file the comments were made on.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "position",
+              "description": "The position in the diff for the commit that the comment was made on.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request this commit comment thread belongs to",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this node.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "RepositoryNode",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestRevisionMarker",
+          "description": "Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.",
+          "fields": [
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lastSeenCommit",
+              "description": "The last commit the viewer has seen.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Commit",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request to which the marker belongs.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BaseRefChangedEvent",
+          "description": "Represents a 'base_ref_changed' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddedToProjectEvent",
+          "description": "Represents a 'added_to_project' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommentDeletedEvent",
+          "description": "Represents a 'comment_deleted' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ConvertedNoteToIssueEvent",
+          "description": "Represents a 'converted_note_to_issue' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "IssueOrPullRequest",
+          "description": "Used for return value of Repository.issueOrPullRequest.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MentionedEvent",
+          "description": "Represents a 'mentioned' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MovedColumnsInProjectEvent",
+          "description": "Represents a 'moved_columns_in_project' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PinnedEvent",
+          "description": "Represents a 'pinned' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "Identifies the issue associated with the event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Issue",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RemovedFromProjectEvent",
+          "description": "Represents a 'removed_from_project' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TransferredEvent",
+          "description": "Represents a 'transferred' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "fromRepository",
+              "description": "The repository this came from",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "Identifies the issue associated with the event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Issue",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnpinnedEvent",
+          "description": "Represents an 'unpinned' event on a given issue or pull request.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "Identifies the actor who performed the event.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "Identifies the issue associated with the event.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Issue",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "PullRequestTimelineItemsItemType",
+          "description": "The possible item types found in a timeline.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "PULL_REQUEST_COMMIT",
+              "description": "Represents a Git commit part of a pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PULL_REQUEST_COMMIT_COMMENT_THREAD",
+              "description": "Represents a commit comment thread part of a pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PULL_REQUEST_REVIEW",
+              "description": "A review object for a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PULL_REQUEST_REVIEW_THREAD",
+              "description": "A threaded list of comments for a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PULL_REQUEST_REVISION_MARKER",
+              "description": "Represents the latest point in the pull request timeline for which the viewer has seen the pull request's commits.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "BASE_REF_CHANGED_EVENT",
+              "description": "Represents a 'base_ref_changed' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "BASE_REF_FORCE_PUSHED_EVENT",
+              "description": "Represents a 'base_ref_force_pushed' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DEPLOYED_EVENT",
+              "description": "Represents a 'deployed' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DEPLOYMENT_ENVIRONMENT_CHANGED_EVENT",
+              "description": "Represents a 'deployment_environment_changed' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "HEAD_REF_DELETED_EVENT",
+              "description": "Represents a 'head_ref_deleted' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "HEAD_REF_FORCE_PUSHED_EVENT",
+              "description": "Represents a 'head_ref_force_pushed' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "HEAD_REF_RESTORED_EVENT",
+              "description": "Represents a 'head_ref_restored' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MERGED_EVENT",
+              "description": "Represents a 'merged' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REVIEW_DISMISSED_EVENT",
+              "description": "Represents a 'review_dismissed' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REVIEW_REQUESTED_EVENT",
+              "description": "Represents an 'review_requested' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REVIEW_REQUEST_REMOVED_EVENT",
+              "description": "Represents an 'review_request_removed' event on a given pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ISSUE_COMMENT",
+              "description": "Represents a comment on an Issue.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CROSS_REFERENCED_EVENT",
+              "description": "Represents a mention made by one issue or pull request to another.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ADDED_TO_PROJECT_EVENT",
+              "description": "Represents a 'added_to_project' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ASSIGNED_EVENT",
+              "description": "Represents an 'assigned' event on any assignable object.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CLOSED_EVENT",
+              "description": "Represents a 'closed' event on any `Closable`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "COMMENT_DELETED_EVENT",
+              "description": "Represents a 'comment_deleted' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CONVERTED_NOTE_TO_ISSUE_EVENT",
+              "description": "Represents a 'converted_note_to_issue' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DEMILESTONED_EVENT",
+              "description": "Represents a 'demilestoned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "LABELED_EVENT",
+              "description": "Represents a 'labeled' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "LOCKED_EVENT",
+              "description": "Represents a 'locked' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MENTIONED_EVENT",
+              "description": "Represents a 'mentioned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MILESTONED_EVENT",
+              "description": "Represents a 'milestoned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MOVED_COLUMNS_IN_PROJECT_EVENT",
+              "description": "Represents a 'moved_columns_in_project' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PINNED_EVENT",
+              "description": "Represents a 'pinned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REFERENCED_EVENT",
+              "description": "Represents a 'referenced' event on a given `ReferencedSubject`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REMOVED_FROM_PROJECT_EVENT",
+              "description": "Represents a 'removed_from_project' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "RENAMED_TITLE_EVENT",
+              "description": "Represents a 'renamed' event on a given issue or pull request",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REOPENED_EVENT",
+              "description": "Represents a 'reopened' event on any `Closable`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SUBSCRIBED_EVENT",
+              "description": "Represents a 'subscribed' event on a given `Subscribable`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "TRANSFERRED_EVENT",
+              "description": "Represents a 'transferred' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNASSIGNED_EVENT",
+              "description": "Represents an 'unassigned' event on any assignable object.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNLABELED_EVENT",
+              "description": "Represents an 'unlabeled' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNLOCKED_EVENT",
+              "description": "Represents an 'unlocked' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "USER_BLOCKED_EVENT",
+              "description": "Represents a 'user_blocked' event on a given user.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNPINNED_EVENT",
+              "description": "Represents an 'unpinned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNSUBSCRIBED_EVENT",
+              "description": "Represents an 'unsubscribed' event on a given `Subscribable`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SuggestedReviewer",
+          "description": "A suggestion to review a pull request based on a user's commit history and review comments.",
+          "fields": [
+            {
+              "name": "isAuthor",
+              "description": "Is this suggestion based on past commits?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isCommenter",
+              "description": "Is this suggestion based on past review comments?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reviewer",
+              "description": "Identifies the user suggested to review the pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ProjectCardArchivedState",
+          "description": "The possible archived states of a project card.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ARCHIVED",
+              "description": "A project card that is archived",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NOT_ARCHIVED",
+              "description": "A project card that is not archived",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueTimelineConnection",
+          "description": "The connection type for IssueTimelineItem.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueTimelineItemEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "IssueTimelineItem",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueTimelineItemEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "IssueTimelineItem",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "IssueTimelineItem",
+          "description": "An item in an issue timeline",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Commit",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CrossReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ClosedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReopenedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "SubscribedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnsubscribedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "AssignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnassignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UserBlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DemilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RenamedTitleEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "TransferredEvent",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueTimelineItemsConnection",
+          "description": "The connection type for IssueTimelineItems.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "IssueTimelineItemsEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "filteredCount",
+              "description": "Identifies the count of items after applying `before` and `after` filters.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "IssueTimelineItems",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageCount",
+              "description": "Identifies the count of items after applying `before`/`after` filters and `first`/`last`/`skip` slicing.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "Identifies the date and time when the timeline was last updated.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueTimelineItemsEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "IssueTimelineItems",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "IssueTimelineItems",
+          "description": "An item in an issue timeline",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "IssueComment",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CrossReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "AddedToProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "AssignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ClosedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CommentDeletedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ConvertedNoteToIssueEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "DemilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "LockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MentionedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MilestonedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MovedColumnsInProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PinnedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReferencedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RemovedFromProjectEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RenamedTitleEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "ReopenedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "SubscribedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "TransferredEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnassignedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlabeledEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UserBlockedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnpinnedEvent",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "UnsubscribedEvent",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "ENUM",
+          "name": "IssueTimelineItemsItemType",
+          "description": "The possible item types found in a timeline.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ISSUE_COMMENT",
+              "description": "Represents a comment on an Issue.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CROSS_REFERENCED_EVENT",
+              "description": "Represents a mention made by one issue or pull request to another.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ADDED_TO_PROJECT_EVENT",
+              "description": "Represents a 'added_to_project' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ASSIGNED_EVENT",
+              "description": "Represents an 'assigned' event on any assignable object.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CLOSED_EVENT",
+              "description": "Represents a 'closed' event on any `Closable`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "COMMENT_DELETED_EVENT",
+              "description": "Represents a 'comment_deleted' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CONVERTED_NOTE_TO_ISSUE_EVENT",
+              "description": "Represents a 'converted_note_to_issue' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DEMILESTONED_EVENT",
+              "description": "Represents a 'demilestoned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "LABELED_EVENT",
+              "description": "Represents a 'labeled' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "LOCKED_EVENT",
+              "description": "Represents a 'locked' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MENTIONED_EVENT",
+              "description": "Represents a 'mentioned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MILESTONED_EVENT",
+              "description": "Represents a 'milestoned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MOVED_COLUMNS_IN_PROJECT_EVENT",
+              "description": "Represents a 'moved_columns_in_project' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PINNED_EVENT",
+              "description": "Represents a 'pinned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REFERENCED_EVENT",
+              "description": "Represents a 'referenced' event on a given `ReferencedSubject`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REMOVED_FROM_PROJECT_EVENT",
+              "description": "Represents a 'removed_from_project' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "RENAMED_TITLE_EVENT",
+              "description": "Represents a 'renamed' event on a given issue or pull request",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REOPENED_EVENT",
+              "description": "Represents a 'reopened' event on any `Closable`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SUBSCRIBED_EVENT",
+              "description": "Represents a 'subscribed' event on a given `Subscribable`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "TRANSFERRED_EVENT",
+              "description": "Represents a 'transferred' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNASSIGNED_EVENT",
+              "description": "Represents an 'unassigned' event on any assignable object.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNLABELED_EVENT",
+              "description": "Represents an 'unlabeled' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNLOCKED_EVENT",
+              "description": "Represents an 'unlocked' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "USER_BLOCKED_EVENT",
+              "description": "Represents a 'user_blocked' event on a given user.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNPINNED_EVENT",
+              "description": "Represents an 'unpinned' event on a given issue or pull request.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNSUBSCRIBED_EVENT",
+              "description": "Represents an 'unsubscribed' event on a given `Subscribable`.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "CollaboratorAffiliation",
+          "description": "Collaborators affiliation level with a subject.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OUTSIDE",
+              "description": "All outside collaborators of an organization-owned subject.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DIRECT",
+              "description": "All collaborators with permissions to an organization-owned subject, regardless of organization membership status.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ALL",
+              "description": "All collaborators the authenticated user can see.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeployKeyConnection",
+          "description": "The connection type for DeployKey.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "DeployKeyEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "DeployKey",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeployKeyEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeployKey",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeployKey",
+          "description": "A repository deploy key.",
+          "fields": [
+            {
+              "name": "createdAt",
+              "description": "Identifies the date and time when the object was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "key",
+              "description": "The deploy key.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "readOnly",
+              "description": "Whether or not the deploy key is read only.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "title",
+              "description": "The deploy key title.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "verified",
+              "description": "Whether or not the deploy key has been verified.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "RepositoryCollaboratorAffiliation",
+          "description": "The affiliation type between collaborator and repository.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ALL",
+              "description": "All collaborators of the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OUTSIDE",
+              "description": "All outside collaborators of an organization-owned repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BranchProtectionRuleConnection",
+          "description": "The connection type for BranchProtectionRule.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "BranchProtectionRuleEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "BranchProtectionRule",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BranchProtectionRuleEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "BranchProtectionRule",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BranchProtectionRule",
+          "description": "A branch protection rule.",
+          "fields": [
+            {
+              "name": "branchProtectionRuleConflicts",
+              "description": "A list of conflicts matching branches protection rule and other branch protection rules",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "BranchProtectionRuleConflictConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "creator",
+              "description": "The actor who created this branch protection rule.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Actor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "dismissesStaleReviews",
+              "description": "Will new commits pushed to matching branches dismiss pull request review approvals.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isAdminEnforced",
+              "description": "Can admins overwrite branch protection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "matchingRefs",
+              "description": "Repository refs that are protected by this rule",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RefConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pattern",
+              "description": "Identifies the protection rule pattern.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pushAllowances",
+              "description": "A list push allowances for this branch protection rule.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PushAllowanceConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository associated with this branch protection rule.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requiredApprovingReviewCount",
+              "description": "Number of approving reviews required to update matching branches.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requiredStatusCheckContexts",
+              "description": "List of required status check contexts that must pass for commits to be accepted to matching branches.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requiresApprovingReviews",
+              "description": "Are approving reviews required to update matching branches.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requiresCommitSignatures",
+              "description": "Are commits required to be signed.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requiresStatusChecks",
+              "description": "Are status checks required to update matching branches.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requiresStrictStatusChecks",
+              "description": "Are branches required to be up to date before merging.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "restrictsPushes",
+              "description": "Is pushing to matching branches restricted.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "restrictsReviewDismissals",
+              "description": "Is dismissal of pull request reviews restricted.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reviewDismissalAllowances",
+              "description": "A list review dismissal allowances for this branch protection rule.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReviewDismissalAllowanceConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewDismissalAllowanceConnection",
+          "description": "The connection type for ReviewDismissalAllowance.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReviewDismissalAllowanceEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ReviewDismissalAllowance",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewDismissalAllowanceEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ReviewDismissalAllowance",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReviewDismissalAllowance",
+          "description": "A team or user who has the ability to dismiss a review on a protected branch.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "The actor that can dismiss.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "ReviewDismissalAllowanceActor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "branchProtectionRule",
+              "description": "Identifies the branch protection rule associated with the allowed user or team.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "BranchProtectionRule",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "ReviewDismissalAllowanceActor",
+          "description": "Types that can be an actor.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Team",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PushAllowanceConnection",
+          "description": "The connection type for PushAllowance.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PushAllowanceEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PushAllowance",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PushAllowanceEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PushAllowance",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PushAllowance",
+          "description": "A team or user who has the ability to push to a protected branch.",
+          "fields": [
+            {
+              "name": "actor",
+              "description": "The actor that can push.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "PushAllowanceActor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "branchProtectionRule",
+              "description": "Identifies the branch protection rule associated with the allowed user or team.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "BranchProtectionRule",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "PushAllowanceActor",
+          "description": "Types that can be an actor.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Team",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RefConnection",
+          "description": "The connection type for Ref.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RefEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Ref",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RefEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BranchProtectionRuleConflictConnection",
+          "description": "The connection type for BranchProtectionRuleConflict.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "BranchProtectionRuleConflictEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "BranchProtectionRuleConflict",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BranchProtectionRuleConflictEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "BranchProtectionRuleConflict",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "BranchProtectionRuleConflict",
+          "description": "A conflict between two branch protection rules.",
+          "fields": [
+            {
+              "name": "branchProtectionRule",
+              "description": "Identifies the branch protection rule.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "BranchProtectionRule",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "conflictingBranchProtectionRule",
+              "description": "Identifies the conflicting branch protection rule.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "BranchProtectionRule",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ref",
+              "description": "Identifies the branch ref that has conflicting rules",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Ref",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MilestoneConnection",
+          "description": "The connection type for Milestone.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "MilestoneEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Milestone",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MilestoneEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Milestone",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "MilestoneOrder",
+          "description": "Ordering options for milestone connections.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order milestones by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "MilestoneOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "MilestoneOrderField",
+          "description": "Properties by which milestone connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "DUE_DATE",
+              "description": "Order milestones by when they are due.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CREATED_AT",
+              "description": "Order milestones by when they were created.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UPDATED_AT",
+              "description": "Order milestones by when they were last updated.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NUMBER",
+              "description": "Order milestones by their number.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CodeOfConduct",
+          "description": "The Code of Conduct for a repository",
+          "fields": [
+            {
+              "name": "body",
+              "description": "The body of the Code of Conduct",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "key",
+              "description": "The key for the Code of Conduct",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The formal name of the Code of Conduct",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this Code of Conduct",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this Code of Conduct",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "URI",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryCollaboratorConnection",
+          "description": "The connection type for User.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "RepositoryCollaboratorEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RepositoryCollaboratorEdge",
+          "description": "Represents a user who is a collaborator of a repository.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "permission",
+              "description": "The permission the user has on the repository.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "RepositoryPermission",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "permissionSources",
+              "description": "A list of sources for the user's access to the repository.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "PermissionSource",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PermissionSource",
+          "description": "A level of permission and source for a user's access to a repository.",
+          "fields": [
+            {
+              "name": "organization",
+              "description": "The organization the repository belongs to.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Organization",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "permission",
+              "description": "The level of access this source has granted to the user.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "DefaultRepositoryPermissionField",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "source",
+              "description": "The source of this permission.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "PermissionGranter",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "PermissionGranter",
+          "description": "Types that can grant permissions on a repository to a user",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Team",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "LanguageOrder",
+          "description": "Ordering options for language connections.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order languages by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "LanguageOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "LanguageOrderField",
+          "description": "Properties by which language connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "SIZE",
+              "description": "Order languages by the size of all files containing the language",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "RefOrder",
+          "description": "Ways in which lists of git refs can be ordered upon return.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field in which to order refs by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "RefOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The direction in which to order refs by the specified field.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "RefOrderField",
+          "description": "Properties by which ref connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "TAG_COMMIT_DATE",
+              "description": "Order refs by underlying commit date if the ref prefix is refs/tags/",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ALPHABETICAL",
+              "description": "Order refs by their alphanumeric name",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityAdvisory",
+          "description": "A GitHub Security Advisory",
+          "fields": [
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": "This is a long plaintext description of the advisory",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ghsaId",
+              "description": "The GitHub Security Advisory ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "identifiers",
+              "description": "A list of identifiers for this advisory",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "SecurityAdvisoryIdentifier",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "origin",
+              "description": "The organization that originated the advisory",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "publishedAt",
+              "description": "When the advisory was published",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "references",
+              "description": "A list of references for this advisory",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "SecurityAdvisoryReference",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "severity",
+              "description": "The severity of the advisory",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "SecurityAdvisorySeverity",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "summary",
+              "description": "A short plaintext summary of the advisory",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "When the advisory was last updated",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "vulnerabilities",
+              "description": "Vulnerabilities associated with this Advisory",
+              "args": [
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for the returned topics.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "SecurityVulnerabilityOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"UPDATED_AT\",direction:\"DESC\"}"
+                },
+                {
+                  "name": "ecosystem",
+                  "description": "An ecosystem to filter vulnerabilities by.",
+                  "type": {
+                    "kind": "ENUM",
+                    "name": "SecurityAdvisoryEcosystem",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "package",
+                  "description": "A package name to filter vulnerabilities by.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "severities",
+                  "description": "A list of severities to filter vulnerabilities by.",
+                  "type": {
+                    "kind": "LIST",
+                    "name": null,
+                    "ofType": {
+                      "kind": "NON_NULL",
+                      "name": null,
+                      "ofType": {
+                        "kind": "ENUM",
+                        "name": "SecurityAdvisorySeverity",
+                        "ofType": null
+                      }
+                    }
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityVulnerabilityConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "withdrawnAt",
+              "description": "When the advisory was withdrawn, if it has been withdrawn",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "DateTime",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "SecurityAdvisorySeverity",
+          "description": "Severity of the vulnerability.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "LOW",
+              "description": "Low.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MODERATE",
+              "description": "Moderate.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "HIGH",
+              "description": "High.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "CRITICAL",
+              "description": "Critical.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityAdvisoryIdentifier",
+          "description": "A GitHub Security Advisory Identifier",
+          "fields": [
+            {
+              "name": "type",
+              "description": "The identifier type, e.g. GHSA, CVE",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "value",
+              "description": "The identifier",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityAdvisoryReference",
+          "description": "A GitHub Security Advisory Reference",
+          "fields": [
+            {
+              "name": "url",
+              "description": "A publicly accessible reference",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityVulnerabilityConnection",
+          "description": "The connection type for SecurityVulnerability.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityVulnerabilityEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityVulnerability",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityVulnerabilityEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "SecurityVulnerability",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityVulnerability",
+          "description": "An individual vulnerability within an Advisory",
+          "fields": [
+            {
+              "name": "advisory",
+              "description": "The Advisory associated with this Vulnerability",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityAdvisory",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "firstPatchedVersion",
+              "description": "The first version containing a fix for the vulnerability",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "SecurityAdvisoryPackageVersion",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "package",
+              "description": "A description of the vulnerable package",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityAdvisoryPackage",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "severity",
+              "description": "The severity of the vulnerability within this package",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "SecurityAdvisorySeverity",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatedAt",
+              "description": "When the vulnerability was last updated",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "vulnerableVersionRange",
+              "description": "A string that describes the vulnerable package versions.\nThis string follows a basic syntax with a few forms.\n+ `= 0.2.0` denotes a single vulnerable version.\n+ `<= 1.0.8` denotes a version range up to and including the specified version\n+ `< 0.1.11` denotes a version range up to, but excluding, the specified version\n+ `>= 4.3.0, < 4.3.5` denotes a version range with a known minimum and maximum version.\n+ `>= 0.0.1` denotes a version range with a known minimum, but no known maximum\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityAdvisoryPackage",
+          "description": "An individual package",
+          "fields": [
+            {
+              "name": "ecosystem",
+              "description": "The ecosystem the package belongs to, e.g. RUBYGEMS, NPM",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "SecurityAdvisoryEcosystem",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The package name",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "SecurityAdvisoryEcosystem",
+          "description": "The possible ecosystems of a security vulnerability's package.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "RUBYGEMS",
+              "description": "Ruby gems hosted at RubyGems.org",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NPM",
+              "description": "JavaScript packages hosted at npmjs.com",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PIP",
+              "description": "Python packages hosted at PyPI.org",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MAVEN",
+              "description": "Java artifacts hosted at the Maven central repository",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NUGET",
+              "description": ".NET packages hosted at the NuGet Gallery",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityAdvisoryPackageVersion",
+          "description": "An individual package version",
+          "fields": [
+            {
+              "name": "identifier",
+              "description": "The package name or version",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "SecurityVulnerabilityOrder",
+          "description": "Ordering options for security vulnerability connections",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order security vulnerabilities by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "SecurityVulnerabilityOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "SecurityVulnerabilityOrderField",
+          "description": "Properties by which security vulnerability connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "UPDATED_AT",
+              "description": "Order vulnerability by update time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "SCALAR",
+          "name": "GitSSHRemote",
+          "description": "Git SSH string",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TopicConnection",
+          "description": "The connection type for Topic.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TopicEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Topic",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TopicEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Topic",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ContributionsCollection",
+          "description": "A contributions collection aggregates contributions such as opened issues and commits created by a user.",
+          "fields": [
+            {
+              "name": "commitContributionsByRepository",
+              "description": "Commit contributions made by the user, grouped by repository.",
+              "args": [
+                {
+                  "name": "maxRepositories",
+                  "description": "How many repositories should be included.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "25"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "CommitContributionsByRepository",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "contributionCalendar",
+              "description": "A calendar of this user's contributions on GitHub.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ContributionCalendar",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "contributionYears",
+              "description": "The years the user has been making contributions with the most recent year first.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "Int",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "doesEndInCurrentMonth",
+              "description": "Determine if this collection's time span ends in the current month.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "earliestRestrictedContributionDate",
+              "description": "The date of the first restricted contribution the user made in this time period. Can only be non-null when the user has enabled private contribution counts.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Date",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "endedAt",
+              "description": "The ending date and time of this collection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "firstIssueContribution",
+              "description": "The first issue the user opened on GitHub. This will be null if that issue was opened outside the collection's time range and ignoreTimeRange is false. If the issue is not visible but the user has opted to show private contributions, a RestrictedContribution will be returned.",
+              "args": [
+                {
+                  "name": "ignoreTimeRange",
+                  "description": "If true, the first issue will be returned even if it was opened outside of the collection's time range.\n\n**Upcoming Change on 2019-07-01 UTC**\n**Description:** `ignoreTimeRange` will be removed. Use a `ContributionsCollection` starting sufficiently far back\n**Reason:** ignore_time_range will be removed\n",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "UNION",
+                "name": "CreatedIssueOrRestrictedContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "firstPullRequestContribution",
+              "description": "The first pull request the user opened on GitHub. This will be null if that pull request was opened outside the collection's time range and ignoreTimeRange is not true. If the pull request is not visible but the user has opted to show private contributions, a RestrictedContribution will be returned.",
+              "args": [
+                {
+                  "name": "ignoreTimeRange",
+                  "description": "If true, the first pull request will be returned even if it was opened outside of the collection's time range.\n\n**Upcoming Change on 2019-07-01 UTC**\n**Description:** `ignoreTimeRange` will be removed. Use a `ContributionsCollection` starting sufficiently far back\n**Reason:** ignore_time_range will be removed\n",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "UNION",
+                "name": "CreatedPullRequestOrRestrictedContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "firstRepositoryContribution",
+              "description": "The first repository the user created on GitHub. This will be null if that first repository was created outside the collection's time range and ignoreTimeRange is false. If the repository is not visible, then a RestrictedContribution is returned.",
+              "args": [
+                {
+                  "name": "ignoreTimeRange",
+                  "description": "If true, the first repository will be returned even if it was opened outside of the collection's time range.\n\n**Upcoming Change on 2019-07-01 UTC**\n**Description:** `ignoreTimeRange` will be removed. Use a `ContributionsCollection` starting sufficiently far back\n**Reason:** ignore_time_range will be removed\n",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "UNION",
+                "name": "CreatedRepositoryOrRestrictedContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasActivityInThePast",
+              "description": "Does the user have any more activity in the timeline that occurred prior to the collection's time range?",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasAnyContributions",
+              "description": "Determine if there are any contributions in this collection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hasAnyRestrictedContributions",
+              "description": "Determine if the user made any contributions in this time frame whose details are not visible because they were made in a private repository. Can only be true if the user enabled private contribution counts.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isSingleDay",
+              "description": "Whether or not the collector's time span is all within the same day.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issueContributions",
+              "description": "A list of issues the user opened.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first issue ever be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "excludePopular",
+                  "description": "Should the user's most commented issue be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for contributions returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ContributionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"OCCURRED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedIssueContributionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issueContributionsByRepository",
+              "description": "Issue contributions made by the user, grouped by repository.",
+              "args": [
+                {
+                  "name": "maxRepositories",
+                  "description": "How many repositories should be included.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "25"
+                },
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first issue ever be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "excludePopular",
+                  "description": "Should the user's most commented issue be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "IssueContributionsByRepository",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "joinedGitHubContribution",
+              "description": "When the user signed up for GitHub. This will be null if that sign up date falls outside the collection's time range and ignoreTimeRange is false.",
+              "args": [
+                {
+                  "name": "ignoreTimeRange",
+                  "description": "If true, the contribution will be returned even if the user signed up outside of the collection's time range.\n\n**Upcoming Change on 2019-07-01 UTC**\n**Description:** `ignoreTimeRange` will be removed. Use a `ContributionsCollection` starting sufficiently far back\n**Reason:** ignore_time_range will be removed\n",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "JoinedGitHubContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "latestRestrictedContributionDate",
+              "description": "The date of the most recent restricted contribution the user made in this time period. Can only be non-null when the user has enabled private contribution counts.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "Date",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mostRecentCollectionWithActivity",
+              "description": "When this collection's time range does not include any activity from the user, use this\nto get a different collection from an earlier time range that does have activity.\n",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ContributionsCollection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mostRecentCollectionWithoutActivity",
+              "description": "Returns a different contributions collection from an earlier time range than this one\nthat does not have any contributions.\n",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ContributionsCollection",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "popularIssueContribution",
+              "description": "The issue the user opened on GitHub that received the most comments in the specified\ntime frame.\n",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreatedIssueContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "popularPullRequestContribution",
+              "description": "The pull request the user opened on GitHub that received the most comments in the\nspecified time frame.\n",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreatedPullRequestContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestContributions",
+              "description": "Pull request contributions made by the user.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first pull request ever be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "excludePopular",
+                  "description": "Should the user's most commented pull request be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for contributions returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ContributionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"OCCURRED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedPullRequestContributionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestContributionsByRepository",
+              "description": "Pull request contributions made by the user, grouped by repository.",
+              "args": [
+                {
+                  "name": "maxRepositories",
+                  "description": "How many repositories should be included.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "25"
+                },
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first pull request ever be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "excludePopular",
+                  "description": "Should the user's most commented pull request be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "PullRequestContributionsByRepository",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReviewContributions",
+              "description": "Pull request review contributions made by the user.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for contributions returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ContributionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"OCCURRED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedPullRequestReviewContributionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReviewContributionsByRepository",
+              "description": "Pull request review contributions made by the user, grouped by repository.",
+              "args": [
+                {
+                  "name": "maxRepositories",
+                  "description": "How many repositories should be included.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": "25"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "PullRequestReviewContributionsByRepository",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositoryContributions",
+              "description": "A list of repositories owned by the user that the user created in this time range.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first repository ever be excluded from the result.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for contributions returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ContributionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"OCCURRED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedRepositoryContributionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "restrictedContributionsCount",
+              "description": "A count of contributions made by the user that the viewer cannot access. Only non-zero when the user has chosen to share their private contribution counts.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "startedAt",
+              "description": "The beginning date and time of this collection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCommitContributions",
+              "description": "How many commits were made by the user in this time span.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalIssueContributions",
+              "description": "How many issues the user opened.",
+              "args": [
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first issue ever be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "excludePopular",
+                  "description": "Should the user's most commented issue be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalPullRequestContributions",
+              "description": "How many pull requests the user opened.",
+              "args": [
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first pull request ever be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "excludePopular",
+                  "description": "Should the user's most commented pull request be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalPullRequestReviewContributions",
+              "description": "How many pull request reviews the user left.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalRepositoriesWithContributedCommits",
+              "description": "How many different repositories the user committed to.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalRepositoriesWithContributedIssues",
+              "description": "How many different repositories the user opened issues in.",
+              "args": [
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first issue ever be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "excludePopular",
+                  "description": "Should the user's most commented issue be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalRepositoriesWithContributedPullRequestReviews",
+              "description": "How many different repositories the user left pull request reviews in.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalRepositoriesWithContributedPullRequests",
+              "description": "How many different repositories the user opened pull requests in.",
+              "args": [
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first pull request ever be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                },
+                {
+                  "name": "excludePopular",
+                  "description": "Should the user's most commented pull request be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalRepositoryContributions",
+              "description": "How many repositories the user created.",
+              "args": [
+                {
+                  "name": "excludeFirst",
+                  "description": "Should the user's first repository ever be excluded from this count.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made the contributions in this collection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedIssueContributionConnection",
+          "description": "The connection type for CreatedIssueContribution.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedIssueContributionEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedIssueContribution",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedIssueContributionEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreatedIssueContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedIssueContribution",
+          "description": "Represents the contribution a user made on GitHub by opening an issue.",
+          "fields": [
+            {
+              "name": "isRestricted",
+              "description": "Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "The issue that was opened.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Issue",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "occurredAt",
+              "description": "When this contribution was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made this contribution.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Contribution",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INTERFACE",
+          "name": "Contribution",
+          "description": "Represents a contribution a user made on GitHub, such as opening an issue.",
+          "fields": [
+            {
+              "name": "isRestricted",
+              "description": "Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "occurredAt",
+              "description": "When this contribution was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made this contribution.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CreatedCommitContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CreatedIssueContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CreatedPullRequestContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CreatedPullRequestReviewContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "CreatedRepositoryContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "JoinedGitHubContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RestrictedContribution",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ContributionOrder",
+          "description": "Ordering options for contribution connections.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field by which to order contributions.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ContributionOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ContributionOrderField",
+          "description": "Properties by which contribution connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OCCURRED_AT",
+              "description": "Order contributions by when they were made.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedRepositoryContributionConnection",
+          "description": "The connection type for CreatedRepositoryContribution.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedRepositoryContributionEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedRepositoryContribution",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedRepositoryContributionEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreatedRepositoryContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedRepositoryContribution",
+          "description": "Represents the contribution a user made on GitHub by creating a repository.",
+          "fields": [
+            {
+              "name": "isRestricted",
+              "description": "Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "occurredAt",
+              "description": "When this contribution was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository that was created.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made this contribution.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Contribution",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "JoinedGitHubContribution",
+          "description": "Represents a user signing up for a GitHub account.",
+          "fields": [
+            {
+              "name": "isRestricted",
+              "description": "Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "occurredAt",
+              "description": "When this contribution was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made this contribution.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Contribution",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "CreatedRepositoryOrRestrictedContribution",
+          "description": "Represents either a repository the viewer can access or a restricted contribution.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CreatedRepositoryContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RestrictedContribution",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RestrictedContribution",
+          "description": "Represents a private contribution a user made on GitHub.",
+          "fields": [
+            {
+              "name": "isRestricted",
+              "description": "Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "occurredAt",
+              "description": "When this contribution was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made this contribution.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Contribution",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "CreatedIssueOrRestrictedContribution",
+          "description": "Represents either a issue the viewer can access or a restricted contribution.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CreatedIssueContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RestrictedContribution",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "UNION",
+          "name": "CreatedPullRequestOrRestrictedContribution",
+          "description": "Represents either a pull request the viewer can access or a restricted contribution.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "CreatedPullRequestContribution",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "RestrictedContribution",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedPullRequestContribution",
+          "description": "Represents the contribution a user made on GitHub by opening a pull request.",
+          "fields": [
+            {
+              "name": "isRestricted",
+              "description": "Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "occurredAt",
+              "description": "When this contribution was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request that was opened.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made this contribution.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Contribution",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ContributionCalendar",
+          "description": "A calendar of contributions made on GitHub by a user.",
+          "fields": [
+            {
+              "name": "colors",
+              "description": "A list of hex color codes used in this calendar. The darker the color, the more contributions it represents.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isHalloween",
+              "description": "Determine if the color set was chosen because it's currently Halloween.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "months",
+              "description": "A list of the months of contributions in this calendar.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "ContributionCalendarMonth",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalContributions",
+              "description": "The count of total contributions in the calendar.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "weeks",
+              "description": "A list of the weeks of contributions in this calendar.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "ContributionCalendarWeek",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ContributionCalendarWeek",
+          "description": "A week of contributions in a user's contribution graph.",
+          "fields": [
+            {
+              "name": "contributionDays",
+              "description": "The days of contributions in this week.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "ContributionCalendarDay",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "firstDay",
+              "description": "The date of the earliest square in this week.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Date",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ContributionCalendarDay",
+          "description": "Represents a single day of contributions on GitHub by a user.",
+          "fields": [
+            {
+              "name": "color",
+              "description": "The hex color code that represents how many contributions were made on this day compared to others in the calendar.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "contributionCount",
+              "description": "How many contributions were made by the user on this day.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "date",
+              "description": "The day this square represents.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Date",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "weekday",
+              "description": "A number representing which day of the week this square represents, e.g., 1 is Monday.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ContributionCalendarMonth",
+          "description": "A month of contributions in a user's contribution graph.",
+          "fields": [
+            {
+              "name": "firstDay",
+              "description": "The date of the first day of this month.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Date",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the month.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalWeeks",
+              "description": "How many weeks started in this month.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "year",
+              "description": "The year the month occurred in.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedPullRequestReviewContributionConnection",
+          "description": "The connection type for CreatedPullRequestReviewContribution.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedPullRequestReviewContributionEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedPullRequestReviewContribution",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedPullRequestReviewContributionEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreatedPullRequestReviewContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedPullRequestReviewContribution",
+          "description": "Represents the contribution a user made by leaving a review on a pull request.",
+          "fields": [
+            {
+              "name": "isRestricted",
+              "description": "Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "occurredAt",
+              "description": "When this contribution was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request the user reviewed.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequest",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReview",
+              "description": "The review the user left on the pull request.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PullRequestReview",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository containing the pull request that the user reviewed.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made this contribution.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Contribution",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestReviewContributionsByRepository",
+          "description": "This aggregates pull request reviews made by a user within one repository.",
+          "fields": [
+            {
+              "name": "contributions",
+              "description": "The pull request review contributions.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for contributions returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ContributionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"OCCURRED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedPullRequestReviewContributionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository in which the pull request reviews were made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CommitContributionsByRepository",
+          "description": "This aggregates commits made by a user within one repository.",
+          "fields": [
+            {
+              "name": "contributions",
+              "description": "The commit contributions, each representing a day.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for commit contributions returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "CommitContributionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"OCCURRED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedCommitContributionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository in which the commits were made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for the user's commits to the repository in this time range.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for the user's commits to the repository in this time range.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedCommitContributionConnection",
+          "description": "The connection type for CreatedCommitContribution.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedCommitContributionEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedCommitContribution",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of commits across days and repositories in the connection.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedCommitContributionEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreatedCommitContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedCommitContribution",
+          "description": "Represents the contribution a user made by committing to a repository.",
+          "fields": [
+            {
+              "name": "commitCount",
+              "description": "How many commits were made on this day to this repository by the user.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isRestricted",
+              "description": "Whether this contribution is associated with a record you do not have access to. For\nexample, your own 'first issue' contribution may have been made on a repository you can no\nlonger access.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "occurredAt",
+              "description": "When this contribution was made.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository the user made a commit in.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resourcePath",
+              "description": "The HTTP path for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "url",
+              "description": "The HTTP URL for this contribution.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "user",
+              "description": "The user who made this contribution.\n",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Contribution",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CommitContributionOrder",
+          "description": "Ordering options for commit contribution connections.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field by which to order commit contributions.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "CommitContributionOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "CommitContributionOrderField",
+          "description": "Properties by which commit contribution connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "OCCURRED_AT",
+              "description": "Order commit contributions by when they were made.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "COMMIT_COUNT",
+              "description": "Order commit contributions by how many commits they represent.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedPullRequestContributionConnection",
+          "description": "The connection type for CreatedPullRequestContribution.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedPullRequestContributionEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedPullRequestContribution",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatedPullRequestContributionEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreatedPullRequestContribution",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PullRequestContributionsByRepository",
+          "description": "This aggregates pull requests opened by a user within one repository.",
+          "fields": [
+            {
+              "name": "contributions",
+              "description": "The pull request contributions.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for contributions returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ContributionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"OCCURRED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedPullRequestContributionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository in which the pull requests were opened.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "IssueContributionsByRepository",
+          "description": "This aggregates issues opened by a user within one repository.",
+          "fields": [
+            {
+              "name": "contributions",
+              "description": "The issue contributions.",
+              "args": [
+                {
+                  "name": "after",
+                  "description": "Returns the elements in the list that come after the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "before",
+                  "description": "Returns the elements in the list that come before the specified cursor.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "first",
+                  "description": "Returns the first _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "last",
+                  "description": "Returns the last _n_ elements from the list.",
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Int",
+                    "ofType": null
+                  },
+                  "defaultValue": null
+                },
+                {
+                  "name": "orderBy",
+                  "description": "Ordering options for contributions returned from the connection.",
+                  "type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ContributionOrder",
+                    "ofType": null
+                  },
+                  "defaultValue": "{field:\"OCCURRED_AT\",direction:\"DESC\"}"
+                }
+              ],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "CreatedIssueContributionConnection",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository in which the issues were opened.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "RepositoryContributionType",
+          "description": "The reason a repository is listed as 'contributed'.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "COMMIT",
+              "description": "Created a commit",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ISSUE",
+              "description": "Created an issue",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PULL_REQUEST",
+              "description": "Created a pull request",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REPOSITORY",
+              "description": "Created the repository",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PULL_REQUEST_REVIEW",
+              "description": "Reviewed a pull request",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PublicKeyConnection",
+          "description": "The connection type for PublicKey.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PublicKeyEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PublicKey",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "PublicKeyEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PublicKey",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "FollowingConnection",
+          "description": "The connection type for User.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "FollowerConnection",
+          "description": "The connection type for User.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "UserEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "User",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "StarredRepositoryConnection",
+          "description": "The connection type for Repository.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "StarredRepositoryEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "StarredRepositoryEdge",
+          "description": "Represents a starred repository.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "starredAt",
+              "description": "Identifies when the item was starred.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AppEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "App",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RateLimit",
+          "description": "Represents the client's rate limit.",
+          "fields": [
+            {
+              "name": "cost",
+              "description": "The point cost for the current query counting against the rate limit.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "limit",
+              "description": "The maximum number of points the client is permitted to consume in a 60 minute window.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodeCount",
+              "description": "The maximum number of nodes this query may return",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "remaining",
+              "description": "The number of points remaining in the current rate limit window.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resetAt",
+              "description": "The time at which the current rate limit window resets in UTC epoch seconds.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "DateTime",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SearchResultItemConnection",
+          "description": "A list of results that matched against a search query.",
+          "fields": [
+            {
+              "name": "codeCount",
+              "description": "The number of pieces of code that matched the search query.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SearchResultItemEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issueCount",
+              "description": "The number of issues that matched the search query.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "UNION",
+                  "name": "SearchResultItem",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repositoryCount",
+              "description": "The number of repositories that matched the search query.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "userCount",
+              "description": "The number of users that matched the search query.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "wikiCount",
+              "description": "The number of wiki pages that matched the search query.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SearchResultItemEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "SearchResultItem",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "textMatches",
+              "description": "Text matches on the result found.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "TextMatch",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "SearchResultItem",
+          "description": "The results of a search.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Issue",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "PullRequest",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "MarketplaceListing",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TextMatch",
+          "description": "A text match within a search result.",
+          "fields": [
+            {
+              "name": "fragment",
+              "description": "The specific text fragment within the property matched on.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "highlights",
+              "description": "Highlights within the matched fragment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "TextMatchHighlight",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "property",
+              "description": "The property matched on.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "TextMatchHighlight",
+          "description": "Represents a single highlight in a search result match.",
+          "fields": [
+            {
+              "name": "beginIndice",
+              "description": "The indice in the fragment where the matched text begins.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "endIndice",
+              "description": "The indice in the fragment where the matched text ends.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "text",
+              "description": "The text matched.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "SearchType",
+          "description": "Represents the individual results of a search.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "ISSUE",
+              "description": "Returns results matching issues in repositories.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REPOSITORY",
+              "description": "Returns results matching repositories.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "USER",
+              "description": "Returns results matching users and organizations on GitHub.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "UNION",
+          "name": "CollectionItemContent",
+          "description": "Types that can be inside Collection Items.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": [
+            {
+              "kind": "OBJECT",
+              "name": "Repository",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "Organization",
+              "ofType": null
+            },
+            {
+              "kind": "OBJECT",
+              "name": "User",
+              "ofType": null
+            }
+          ]
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GitHubMetadata",
+          "description": "Represents information about the GitHub instance.",
+          "fields": [
+            {
+              "name": "gitHubServicesSha",
+              "description": "Returns a String that's a SHA of `github-services`",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "gitIpAddresses",
+              "description": "IP addresses that users connect to for git operations",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "hookIpAddresses",
+              "description": "IP addresses that service hooks are sent from",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "importerIpAddresses",
+              "description": "IP addresses that the importer connects from",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isPasswordAuthenticationVerifiable",
+              "description": "Whether or not users are verified",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pagesIpAddresses",
+              "description": "IP addresses for GitHub Pages' A records",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityAdvisoryConnection",
+          "description": "The connection type for SecurityAdvisory.",
+          "fields": [
+            {
+              "name": "edges",
+              "description": "A list of edges.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityAdvisoryEdge",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "nodes",
+              "description": "A list of nodes.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "SecurityAdvisory",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pageInfo",
+              "description": "Information to aid in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "PageInfo",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "totalCount",
+              "description": "Identifies the total count of items in the connection.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SecurityAdvisoryEdge",
+          "description": "An edge in a connection.",
+          "fields": [
+            {
+              "name": "cursor",
+              "description": "A cursor for use in pagination.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "node",
+              "description": "The item at the end of the edge.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "SecurityAdvisory",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "SecurityAdvisoryOrder",
+          "description": "Ordering options for security advisory connections",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "field",
+              "description": "The field to order security advisories by.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "SecurityAdvisoryOrderField",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "direction",
+              "description": "The ordering direction.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "OrderDirection",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "SecurityAdvisoryOrderField",
+          "description": "Properties by which security advisory connections can be ordered.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "PUBLISHED_AT",
+              "description": "Order advisories by publication time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UPDATED_AT",
+              "description": "Order advisories by update time",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "SecurityAdvisoryIdentifierFilter",
+          "description": "An advisory identifier to filter results on.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "type",
+              "description": "The identifier type.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "SecurityAdvisoryIdentifierType",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "value",
+              "description": "The identifier string. Supports exact or partial matching.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "SecurityAdvisoryIdentifierType",
+          "description": "Identifier formats available for advisories.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "CVE",
+              "description": "Common Vulnerabilities and Exposures Identifier.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "GHSA",
+              "description": "GitHub Security Advisory ID.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Mutation",
+          "description": "The root query for implementing GraphQL mutations.",
+          "fields": [
+            {
+              "name": "acceptTopicSuggestion",
+              "description": "Applies a suggested topic to the repository.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AcceptTopicSuggestionInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AcceptTopicSuggestionPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addAssigneesToAssignable",
+              "description": "Adds assignees to an assignable object.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddAssigneesToAssignableInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddAssigneesToAssignablePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addComment",
+              "description": "Adds a comment to an Issue or Pull Request.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddCommentInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddCommentPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addLabelsToLabelable",
+              "description": "Adds labels to a labelable object.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddLabelsToLabelableInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddLabelsToLabelablePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addProjectCard",
+              "description": "Adds a card to a ProjectColumn. Either `contentId` or `note` must be provided but **not** both.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddProjectCardInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddProjectCardPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addProjectColumn",
+              "description": "Adds a column to a Project.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddProjectColumnInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddProjectColumnPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addPullRequestReview",
+              "description": "Adds a review to a Pull Request.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddPullRequestReviewInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddPullRequestReviewPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addPullRequestReviewComment",
+              "description": "Adds a comment to a review.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddPullRequestReviewCommentInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddPullRequestReviewCommentPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addReaction",
+              "description": "Adds a reaction to a subject.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddReactionInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddReactionPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "addStar",
+              "description": "Adds a star to a Starrable.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "AddStarInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "AddStarPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "changeUserStatus",
+              "description": "Update your status on GitHub.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "ChangeUserStatusInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ChangeUserStatusPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "clearLabelsFromLabelable",
+              "description": "Clears all labels from a labelable object.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "ClearLabelsFromLabelableInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ClearLabelsFromLabelablePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "cloneProject",
+              "description": "Creates a new project by cloning configuration from an existing project.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "CloneProjectInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CloneProjectPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closeIssue",
+              "description": "Close an issue.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "CloseIssueInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CloseIssuePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "closePullRequest",
+              "description": "Close a pull request.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "ClosePullRequestInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ClosePullRequestPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "convertProjectCardNoteToIssue",
+              "description": "Convert a project note card to one associated with a newly created issue.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "ConvertProjectCardNoteToIssueInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ConvertProjectCardNoteToIssuePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createBranchProtectionRule",
+              "description": "Create a new branch protection rule",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "CreateBranchProtectionRuleInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreateBranchProtectionRulePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createIssue",
+              "description": "Creates a new issue.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "CreateIssueInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreateIssuePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createProject",
+              "description": "Creates a new project.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "CreateProjectInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreateProjectPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "createPullRequest",
+              "description": "Create a new pull request",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "CreatePullRequestInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "CreatePullRequestPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "declineTopicSuggestion",
+              "description": "Rejects a suggested topic for the repository.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeclineTopicSuggestionInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeclineTopicSuggestionPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deleteBranchProtectionRule",
+              "description": "Delete a branch protection rule",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeleteBranchProtectionRuleInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeleteBranchProtectionRulePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deleteIssue",
+              "description": "Deletes an Issue object.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeleteIssueInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeleteIssuePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deleteIssueComment",
+              "description": "Deletes an IssueComment object.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeleteIssueCommentInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeleteIssueCommentPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deleteProject",
+              "description": "Deletes a project.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeleteProjectInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeleteProjectPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deleteProjectCard",
+              "description": "Deletes a project card.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeleteProjectCardInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeleteProjectCardPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deleteProjectColumn",
+              "description": "Deletes a project column.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeleteProjectColumnInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeleteProjectColumnPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletePullRequestReview",
+              "description": "Deletes a pull request review.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeletePullRequestReviewInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeletePullRequestReviewPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletePullRequestReviewComment",
+              "description": "Deletes a pull request review comment.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DeletePullRequestReviewCommentInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DeletePullRequestReviewCommentPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "dismissPullRequestReview",
+              "description": "Dismisses an approved or rejected pull request review.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "DismissPullRequestReviewInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "DismissPullRequestReviewPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lockLockable",
+              "description": "Lock a lockable object",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "LockLockableInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "LockLockablePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mergePullRequest",
+              "description": "Merge a pull request.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "MergePullRequestInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "MergePullRequestPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "moveProjectCard",
+              "description": "Moves a project card to another place.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "MoveProjectCardInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "MoveProjectCardPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "moveProjectColumn",
+              "description": "Moves a project column to another place.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "MoveProjectColumnInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "MoveProjectColumnPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "removeAssigneesFromAssignable",
+              "description": "Removes assignees from an assignable object.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "RemoveAssigneesFromAssignableInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RemoveAssigneesFromAssignablePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "removeLabelsFromLabelable",
+              "description": "Removes labels from a Labelable object.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "RemoveLabelsFromLabelableInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RemoveLabelsFromLabelablePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "removeOutsideCollaborator",
+              "description": "Removes outside collaborator from all repositories in an organization.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "RemoveOutsideCollaboratorInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RemoveOutsideCollaboratorPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "removeReaction",
+              "description": "Removes a reaction from a subject.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "RemoveReactionInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RemoveReactionPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "removeStar",
+              "description": "Removes a star from a Starrable.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "RemoveStarInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RemoveStarPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reopenIssue",
+              "description": "Reopen a issue.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "ReopenIssueInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ReopenIssuePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reopenPullRequest",
+              "description": "Reopen a pull request.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "ReopenPullRequestInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ReopenPullRequestPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requestReviews",
+              "description": "Set review requests on a pull request.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "RequestReviewsInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "RequestReviewsPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "resolveReviewThread",
+              "description": "Marks a review thread as resolved.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "ResolveReviewThreadInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ResolveReviewThreadPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "submitPullRequestReview",
+              "description": "Submits a pending pull request review.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "SubmitPullRequestReviewInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "SubmitPullRequestReviewPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "unlockLockable",
+              "description": "Unlock a lockable object",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UnlockLockableInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UnlockLockablePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "unmarkIssueAsDuplicate",
+              "description": "Unmark an issue as a duplicate of another issue.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UnmarkIssueAsDuplicateInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UnmarkIssueAsDuplicatePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "unresolveReviewThread",
+              "description": "Marks a review thread as unresolved.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UnresolveReviewThreadInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UnresolveReviewThreadPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updateBranchProtectionRule",
+              "description": "Create a new branch protection rule",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdateBranchProtectionRuleInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdateBranchProtectionRulePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updateIssue",
+              "description": "Updates an Issue.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdateIssueInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdateIssuePayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updateIssueComment",
+              "description": "Updates an IssueComment object.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdateIssueCommentInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdateIssueCommentPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updateProject",
+              "description": "Updates an existing project.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdateProjectInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdateProjectPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updateProjectCard",
+              "description": "Updates an existing project card.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdateProjectCardInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdateProjectCardPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updateProjectColumn",
+              "description": "Updates an existing project column.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdateProjectColumnInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdateProjectColumnPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatePullRequest",
+              "description": "Update a pull request",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdatePullRequestInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdatePullRequestPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatePullRequestReview",
+              "description": "Updates the body of a pull request review.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdatePullRequestReviewInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdatePullRequestReviewPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updatePullRequestReviewComment",
+              "description": "Updates a pull request review comment.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdatePullRequestReviewCommentInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdatePullRequestReviewCommentPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updateSubscription",
+              "description": "Updates the state for subscribable subjects.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdateSubscriptionInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdateSubscriptionPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "updateTopics",
+              "description": "Replaces the repository's topics with the given topics.",
+              "args": [
+                {
+                  "name": "input",
+                  "description": null,
+                  "type": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "UpdateTopicsInput",
+                      "ofType": null
+                    }
+                  },
+                  "defaultValue": null
+                }
+              ],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UpdateTopicsPayload",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddReactionPayload",
+          "description": "Autogenerated return type of AddReaction",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reaction",
+              "description": "The reaction object.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Reaction",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "The reactable subject.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Reactable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddReactionInput",
+          "description": "Autogenerated input type of AddReaction",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "subjectId",
+              "description": "The Node ID of the subject to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "content",
+              "description": "The name of the emoji to react with.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ReactionContent",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RemoveReactionPayload",
+          "description": "Autogenerated return type of RemoveReaction",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reaction",
+              "description": "The reaction object.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Reaction",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "The reactable subject.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Reactable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "RemoveReactionInput",
+          "description": "Autogenerated input type of RemoveReaction",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "subjectId",
+              "description": "The Node ID of the subject to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "content",
+              "description": "The name of the emoji reaction to remove.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ReactionContent",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdateSubscriptionPayload",
+          "description": "Autogenerated return type of UpdateSubscription",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subscribable",
+              "description": "The input subscribable entity.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Subscribable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdateSubscriptionInput",
+          "description": "Autogenerated input type of UpdateSubscription",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "subscribableId",
+              "description": "The Node ID of the subscribable object to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "state",
+              "description": "The new state of the subscription.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "SubscriptionState",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddCommentPayload",
+          "description": "Autogenerated return type of AddComment",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commentEdge",
+              "description": "The edge from the subject's comment connection.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "IssueCommentEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subject",
+              "description": "The subject",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Node",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "timelineEdge",
+              "description": "The edge from the subject's timeline connection.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "IssueTimelineItemEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddCommentInput",
+          "description": "Autogenerated input type of AddComment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "subjectId",
+              "description": "The Node ID of the subject to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The contents of the comment.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "MinimizeCommentInput",
+          "description": "Autogenerated input type of MinimizeComment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "subjectId",
+              "description": "The Node ID of the subject to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "classifier",
+              "description": "The classification of comment",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "ReportedContentClassifiers",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "ReportedContentClassifiers",
+          "description": "The reasons a piece of content can be reported or minimized.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "SPAM",
+              "description": "A spammy piece of content",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ABUSE",
+              "description": "An abusive or harassing piece of content",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OFF_TOPIC",
+              "description": "An irrelevant piece of content",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OUTDATED",
+              "description": "An outdated piece of content",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "RESOLVED",
+              "description": "The content has been resolved",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UnminimizeCommentInput",
+          "description": "Autogenerated input type of UnminimizeComment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "subjectId",
+              "description": "The Node ID of the subject to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdateIssueCommentPayload",
+          "description": "Autogenerated return type of UpdateIssueComment",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issueComment",
+              "description": "The updated comment.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "IssueComment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdateIssueCommentInput",
+          "description": "Autogenerated input type of UpdateIssueComment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "id",
+              "description": "The ID of the IssueComment to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The updated text of the comment.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreateProjectPayload",
+          "description": "Autogenerated return type of CreateProject",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "The new project.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CreateProjectInput",
+          "description": "Autogenerated input type of CreateProject",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "ownerId",
+              "description": "The owner ID to create the project under.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "name",
+              "description": "The name of project.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The description of project.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdateProjectPayload",
+          "description": "Autogenerated return type of UpdateProject",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "The updated project.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdateProjectInput",
+          "description": "Autogenerated input type of UpdateProject",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "projectId",
+              "description": "The Project ID to update.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "name",
+              "description": "The name of project.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The description of project.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "state",
+              "description": "Whether the project is open or closed.",
+              "type": {
+                "kind": "ENUM",
+                "name": "ProjectState",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "public",
+              "description": "Whether the project is public or not.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeleteProjectPayload",
+          "description": "Autogenerated return type of DeleteProject",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "owner",
+              "description": "The repository or organization the project was removed from.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "ProjectOwner",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeleteProjectInput",
+          "description": "Autogenerated input type of DeleteProject",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "projectId",
+              "description": "The Project ID to update.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CloneProjectPayload",
+          "description": "Autogenerated return type of CloneProject",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "jobStatusId",
+              "description": "The id of the JobStatus for populating cloned fields.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "The new cloned project.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CloneProjectInput",
+          "description": "Autogenerated input type of CloneProject",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "targetOwnerId",
+              "description": "The owner ID to create the project under.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "sourceId",
+              "description": "The source project to clone.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "includeWorkflows",
+              "description": "Whether or not to clone the source project's workflows.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the project.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The description of the project.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "public",
+              "description": "The visibility of the project, defaults to false (private).",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ImportProjectInput",
+          "description": "Autogenerated input type of ImportProject",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "ownerName",
+              "description": "The name of the Organization or User to create the Project under.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "name",
+              "description": "The name of Project.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The description of Project.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "public",
+              "description": "Whether the Project is public or not.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": "false"
+            },
+            {
+              "name": "columnImports",
+              "description": "A list of columns containing issues and pull requests.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "INPUT_OBJECT",
+                      "name": "ProjectColumnImport",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ProjectColumnImport",
+          "description": "A project column and a list of its issues and PRs.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "columnName",
+              "description": "The name of the column.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "position",
+              "description": "The position of the column, starting from 0.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "issues",
+              "description": "A list of issues and pull requests in the column.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "ProjectCardImport",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ProjectCardImport",
+          "description": "An issue or PR and its owning repository to be used in a project card.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "repository",
+              "description": "Repository name with owner (owner/repository).",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "number",
+              "description": "The issue or pull request number.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddProjectColumnPayload",
+          "description": "Autogenerated return type of AddProjectColumn",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "columnEdge",
+              "description": "The edge from the project's column connection.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectColumnEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "The project",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddProjectColumnInput",
+          "description": "Autogenerated input type of AddProjectColumn",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "projectId",
+              "description": "The Node ID of the project.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the column.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MoveProjectColumnPayload",
+          "description": "Autogenerated return type of MoveProjectColumn",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "columnEdge",
+              "description": "The new edge of the moved column.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectColumnEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "MoveProjectColumnInput",
+          "description": "Autogenerated input type of MoveProjectColumn",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "columnId",
+              "description": "The id of the column to move.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "afterColumnId",
+              "description": "Place the new column after the column with this id. Pass null to place it at the front.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdateProjectColumnPayload",
+          "description": "Autogenerated return type of UpdateProjectColumn",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectColumn",
+              "description": "The updated project column.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectColumn",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdateProjectColumnInput",
+          "description": "Autogenerated input type of UpdateProjectColumn",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "projectColumnId",
+              "description": "The ProjectColumn ID to update.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "name",
+              "description": "The name of project column.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeleteProjectColumnPayload",
+          "description": "Autogenerated return type of DeleteProjectColumn",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletedColumnId",
+              "description": "The deleted column ID.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "project",
+              "description": "The project the deleted column was in.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Project",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeleteProjectColumnInput",
+          "description": "Autogenerated input type of DeleteProjectColumn",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "columnId",
+              "description": "The id of the column to delete.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddProjectCardPayload",
+          "description": "Autogenerated return type of AddProjectCard",
+          "fields": [
+            {
+              "name": "cardEdge",
+              "description": "The edge from the ProjectColumn's card connection.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectCardEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectColumn",
+              "description": "The ProjectColumn",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectColumn",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddProjectCardInput",
+          "description": "Autogenerated input type of AddProjectCard",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "projectColumnId",
+              "description": "The Node ID of the ProjectColumn.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "contentId",
+              "description": "The content of the card. Must be a member of the ProjectCardItem union",
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "note",
+              "description": "The note on the card.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdateProjectCardPayload",
+          "description": "Autogenerated return type of UpdateProjectCard",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectCard",
+              "description": "The updated ProjectCard.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectCard",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdateProjectCardInput",
+          "description": "Autogenerated input type of UpdateProjectCard",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "projectCardId",
+              "description": "The ProjectCard ID to update.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "isArchived",
+              "description": "Whether or not the ProjectCard should be archived",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "note",
+              "description": "The note of ProjectCard.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MoveProjectCardPayload",
+          "description": "Autogenerated return type of MoveProjectCard",
+          "fields": [
+            {
+              "name": "cardEdge",
+              "description": "The new edge of the moved card.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectCardEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "MoveProjectCardInput",
+          "description": "Autogenerated input type of MoveProjectCard",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "cardId",
+              "description": "The id of the card to move.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "columnId",
+              "description": "The id of the column to move it into.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "afterCardId",
+              "description": "Place the new card after the card with this id. Pass null to place it at the top.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeleteProjectCardPayload",
+          "description": "Autogenerated return type of DeleteProjectCard",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "column",
+              "description": "The column the deleted card was in.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectColumn",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deletedCardId",
+              "description": "The deleted card ID.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeleteProjectCardInput",
+          "description": "Autogenerated input type of DeleteProjectCard",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "cardId",
+              "description": "The id of the card to delete.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ConvertProjectCardNoteToIssuePayload",
+          "description": "Autogenerated return type of ConvertProjectCardNoteToIssue",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "projectCard",
+              "description": "The updated ProjectCard.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "ProjectCard",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ConvertProjectCardNoteToIssueInput",
+          "description": "Autogenerated input type of ConvertProjectCardNoteToIssue",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "projectCardId",
+              "description": "The ProjectCard ID to convert.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "repositoryId",
+              "description": "The ID of the repository to create the issue in.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "title",
+              "description": "The title of the newly created issue. Defaults to the card's note text.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The body of the newly created issue.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnmarkIssueAsDuplicatePayload",
+          "description": "Autogenerated return type of UnmarkIssueAsDuplicate",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "duplicate",
+              "description": "The issue or pull request that was marked as a duplicate.",
+              "args": [],
+              "type": {
+                "kind": "UNION",
+                "name": "IssueOrPullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UnmarkIssueAsDuplicateInput",
+          "description": "Autogenerated input type of UnmarkIssueAsDuplicate",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "duplicateId",
+              "description": "ID of the issue or pull request currently marked as a duplicate.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "canonicalId",
+              "description": "ID of the issue or pull request currently considered canonical/authoritative/original.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "LockLockablePayload",
+          "description": "Autogenerated return type of LockLockable",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "lockedRecord",
+              "description": "The item that was locked.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Lockable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "LockLockableInput",
+          "description": "Autogenerated input type of LockLockable",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "lockableId",
+              "description": "ID of the issue or pull request to be locked.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "lockReason",
+              "description": "A reason for why the issue or pull request will be locked.",
+              "type": {
+                "kind": "ENUM",
+                "name": "LockReason",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnlockLockablePayload",
+          "description": "Autogenerated return type of UnlockLockable",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "unlockedRecord",
+              "description": "The item that was unlocked.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Lockable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UnlockLockableInput",
+          "description": "Autogenerated input type of UnlockLockable",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "lockableId",
+              "description": "ID of the issue or pull request to be unlocked.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddAssigneesToAssignablePayload",
+          "description": "Autogenerated return type of AddAssigneesToAssignable",
+          "fields": [
+            {
+              "name": "assignable",
+              "description": "The item that was assigned.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Assignable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddAssigneesToAssignableInput",
+          "description": "Autogenerated input type of AddAssigneesToAssignable",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "assignableId",
+              "description": "The id of the assignable object to add assignees to.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "assigneeIds",
+              "description": "The id of users to add as assignees.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "ID",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RemoveAssigneesFromAssignablePayload",
+          "description": "Autogenerated return type of RemoveAssigneesFromAssignable",
+          "fields": [
+            {
+              "name": "assignable",
+              "description": "The item that was unassigned.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Assignable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "RemoveAssigneesFromAssignableInput",
+          "description": "Autogenerated input type of RemoveAssigneesFromAssignable",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "assignableId",
+              "description": "The id of the assignable object to remove assignees from.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "assigneeIds",
+              "description": "The id of users to remove as assignees.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "ID",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddLabelsToLabelablePayload",
+          "description": "Autogenerated return type of AddLabelsToLabelable",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "labelable",
+              "description": "The item that was labeled.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Labelable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddLabelsToLabelableInput",
+          "description": "Autogenerated input type of AddLabelsToLabelable",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "labelableId",
+              "description": "The id of the labelable object to add labels to.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "labelIds",
+              "description": "The ids of the labels to add.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "ID",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreateIssuePayload",
+          "description": "Autogenerated return type of CreateIssue",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "The new issue.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Issue",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CreateIssueInput",
+          "description": "Autogenerated input type of CreateIssue",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "repositoryId",
+              "description": "The Node ID of the repository.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "title",
+              "description": "The title for the issue.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The body for the issue description.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "assigneeIds",
+              "description": "The Node ID for the user assignee for this issue.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "milestoneId",
+              "description": "The Node ID of the milestone for this issue.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "labelIds",
+              "description": "An array of Node IDs of labels for this issue.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "projectIds",
+              "description": "An array of Node IDs for projects associated with this issue.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ClearLabelsFromLabelablePayload",
+          "description": "Autogenerated return type of ClearLabelsFromLabelable",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "labelable",
+              "description": "The item that was unlabeled.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Labelable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ClearLabelsFromLabelableInput",
+          "description": "Autogenerated input type of ClearLabelsFromLabelable",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "labelableId",
+              "description": "The id of the labelable object to clear the labels from.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RemoveLabelsFromLabelablePayload",
+          "description": "Autogenerated return type of RemoveLabelsFromLabelable",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "labelable",
+              "description": "The Labelable the labels were removed from.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Labelable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "RemoveLabelsFromLabelableInput",
+          "description": "Autogenerated input type of RemoveLabelsFromLabelable",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "labelableId",
+              "description": "The id of the Labelable to remove labels from.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "labelIds",
+              "description": "The ids of labels to remove.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "ID",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CloseIssuePayload",
+          "description": "Autogenerated return type of CloseIssue",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "The issue that was closed.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Issue",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CloseIssueInput",
+          "description": "Autogenerated input type of CloseIssue",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "issueId",
+              "description": "ID of the issue to be closed.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReopenIssuePayload",
+          "description": "Autogenerated return type of ReopenIssue",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "The issue that was opened.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Issue",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ReopenIssueInput",
+          "description": "Autogenerated input type of ReopenIssue",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "issueId",
+              "description": "ID of the issue to be opened.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeleteIssueCommentPayload",
+          "description": "Autogenerated return type of DeleteIssueComment",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeleteIssueCommentInput",
+          "description": "Autogenerated input type of DeleteIssueComment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "id",
+              "description": "The ID of the comment to delete.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdateIssuePayload",
+          "description": "Autogenerated return type of UpdateIssue",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "issue",
+              "description": "The issue.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Issue",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdateIssueInput",
+          "description": "Autogenerated input type of UpdateIssue",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "id",
+              "description": "The ID of the Issue to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "title",
+              "description": "The title for the issue.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The body for the issue description.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "assigneeIds",
+              "description": "An array of Node IDs of users for this issue.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "milestoneId",
+              "description": "The Node ID of the milestone for this issue.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "labelIds",
+              "description": "An array of Node IDs of labels for this issue.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "state",
+              "description": "The desired issue state.",
+              "type": {
+                "kind": "ENUM",
+                "name": "IssueState",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "projectIds",
+              "description": "An array of Node IDs for projects associated with this issue.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeleteIssuePayload",
+          "description": "Autogenerated return type of DeleteIssue",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The repository the issue belonged to",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeleteIssueInput",
+          "description": "Autogenerated input type of DeleteIssue",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "issueId",
+              "description": "The ID of the issue to delete.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "PinIssueInput",
+          "description": "Autogenerated input type of PinIssue",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "issueId",
+              "description": "The ID of the issue to be pinned",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UnpinIssueInput",
+          "description": "Autogenerated input type of UnpinIssue",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "issueId",
+              "description": "The ID of the issue to be unpinned",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreatePullRequestPayload",
+          "description": "Autogenerated return type of CreatePullRequest",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The new pull request.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CreatePullRequestInput",
+          "description": "Autogenerated input type of CreatePullRequest",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "repositoryId",
+              "description": "The Node ID of the repository.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "baseRefName",
+              "description": "The name of the branch you want your changes pulled into. This should be an existing branch\non the current repository. You cannot update the base branch on a pull request to point\nto another repository.\n",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "headRefName",
+              "description": "The name of the branch where your changes are implemented. For cross-repository pull requests\nin the same network, namespace `head_ref_name` with a user like this: `username:branch`.\n",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "title",
+              "description": "The title of the pull request.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The contents of the pull request.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "maintainerCanModify",
+              "description": "Indicates whether maintainers can modify the pull request.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": "true"
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdatePullRequestPayload",
+          "description": "Autogenerated return type of UpdatePullRequest",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The updated pull request.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdatePullRequestInput",
+          "description": "Autogenerated input type of UpdatePullRequest",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestId",
+              "description": "The Node ID of the pull request.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "baseRefName",
+              "description": "The name of the branch you want your changes pulled into. This should be an existing branch\non the current repository.\n",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "title",
+              "description": "The title of the pull request.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The contents of the pull request.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "maintainerCanModify",
+              "description": "Indicates whether maintainers can modify the pull request.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ClosePullRequestPayload",
+          "description": "Autogenerated return type of ClosePullRequest",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request that was closed.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ClosePullRequestInput",
+          "description": "Autogenerated input type of ClosePullRequest",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestId",
+              "description": "ID of the pull request to be closed.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ReopenPullRequestPayload",
+          "description": "Autogenerated return type of ReopenPullRequest",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request that was reopened.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ReopenPullRequestInput",
+          "description": "Autogenerated input type of ReopenPullRequest",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestId",
+              "description": "ID of the pull request to be reopened.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "MergePullRequestPayload",
+          "description": "Autogenerated return type of MergePullRequest",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request that was merged.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "MergePullRequestInput",
+          "description": "Autogenerated input type of MergePullRequest",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestId",
+              "description": "ID of the pull request to be merged.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "commitHeadline",
+              "description": "Commit headline to use for the merge commit; if omitted, a default message will be used.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "commitBody",
+              "description": "Commit body to use for the merge commit; if omitted, a default message will be used",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "expectedHeadOid",
+              "description": "OID that the pull request head ref must match to allow merge; if omitted, no check is performed.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "GitObjectID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeletePullRequestReviewCommentPayload",
+          "description": "Autogenerated return type of DeletePullRequestReviewComment",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReview",
+              "description": "The pull request review the deleted comment belonged to.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeletePullRequestReviewCommentInput",
+          "description": "Autogenerated input type of DeletePullRequestReviewComment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "id",
+              "description": "The ID of the comment to delete.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddPullRequestReviewPayload",
+          "description": "Autogenerated return type of AddPullRequestReview",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReview",
+              "description": "The newly created pull request review.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reviewEdge",
+              "description": "The edge from the pull request's review connection.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddPullRequestReviewInput",
+          "description": "Autogenerated input type of AddPullRequestReview",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestId",
+              "description": "The Node ID of the pull request to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "commitOID",
+              "description": "The commit OID the review pertains to.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "GitObjectID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The contents of the review body comment.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "event",
+              "description": "The event to perform on the pull request review.",
+              "type": {
+                "kind": "ENUM",
+                "name": "PullRequestReviewEvent",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "comments",
+              "description": "The review line comments.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "INPUT_OBJECT",
+                  "name": "DraftPullRequestReviewComment",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "PullRequestReviewEvent",
+          "description": "The possible events to perform on a pull request review.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "COMMENT",
+              "description": "Submit general feedback without explicit approval.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "APPROVE",
+              "description": "Submit feedback and approve merging these changes.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "REQUEST_CHANGES",
+              "description": "Submit feedback that must be addressed before merging.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "DISMISS",
+              "description": "Dismiss review so it now longer effects merging.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DraftPullRequestReviewComment",
+          "description": "Specifies a review comment to be left with a Pull Request Review.",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "path",
+              "description": "Path to the file being commented on.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "position",
+              "description": "Position in the file to leave a comment on.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "Body of the comment to leave.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SubmitPullRequestReviewPayload",
+          "description": "Autogenerated return type of SubmitPullRequestReview",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReview",
+              "description": "The submitted pull request review.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "SubmitPullRequestReviewInput",
+          "description": "Autogenerated input type of SubmitPullRequestReview",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestReviewId",
+              "description": "The Pull Request Review ID to submit.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "event",
+              "description": "The event to send to the Pull Request Review.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "PullRequestReviewEvent",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The text field to set on the Pull Request Review.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdatePullRequestReviewPayload",
+          "description": "Autogenerated return type of UpdatePullRequestReview",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReview",
+              "description": "The updated pull request review.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdatePullRequestReviewInput",
+          "description": "Autogenerated input type of UpdatePullRequestReview",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestReviewId",
+              "description": "The Node ID of the pull request review to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The contents of the pull request review body.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DismissPullRequestReviewPayload",
+          "description": "Autogenerated return type of DismissPullRequestReview",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReview",
+              "description": "The dismissed pull request review.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DismissPullRequestReviewInput",
+          "description": "Autogenerated input type of DismissPullRequestReview",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestReviewId",
+              "description": "The Node ID of the pull request review to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "message",
+              "description": "The contents of the pull request review dismissal message.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeletePullRequestReviewPayload",
+          "description": "Autogenerated return type of DeletePullRequestReview",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReview",
+              "description": "The deleted pull request review.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReview",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeletePullRequestReviewInput",
+          "description": "Autogenerated input type of DeletePullRequestReview",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestReviewId",
+              "description": "The Node ID of the pull request review to delete.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ResolveReviewThreadPayload",
+          "description": "Autogenerated return type of ResolveReviewThread",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "thread",
+              "description": "The thread to resolve.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewThread",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ResolveReviewThreadInput",
+          "description": "Autogenerated input type of ResolveReviewThread",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "threadId",
+              "description": "The ID of the thread to resolve",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnresolveReviewThreadPayload",
+          "description": "Autogenerated return type of UnresolveReviewThread",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "thread",
+              "description": "The thread to resolve.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewThread",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UnresolveReviewThreadInput",
+          "description": "Autogenerated input type of UnresolveReviewThread",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "threadId",
+              "description": "The ID of the thread to unresolve",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddPullRequestReviewCommentPayload",
+          "description": "Autogenerated return type of AddPullRequestReviewComment",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "comment",
+              "description": "The newly created comment.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewComment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commentEdge",
+              "description": "The edge from the review's comment connection.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewCommentEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddPullRequestReviewCommentInput",
+          "description": "Autogenerated input type of AddPullRequestReviewComment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestReviewId",
+              "description": "The Node ID of the review to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "commitOID",
+              "description": "The SHA of the commit to comment on.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "GitObjectID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The text of the comment.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "path",
+              "description": "The relative path of the file to comment on.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "position",
+              "description": "The line index in the diff to comment on.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "inReplyTo",
+              "description": "The comment id to reply to.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdatePullRequestReviewCommentPayload",
+          "description": "Autogenerated return type of UpdatePullRequestReviewComment",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequestReviewComment",
+              "description": "The updated comment.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequestReviewComment",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdatePullRequestReviewCommentInput",
+          "description": "Autogenerated input type of UpdatePullRequestReviewComment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestReviewCommentId",
+              "description": "The Node ID of the comment to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The text of the comment.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RemoveOutsideCollaboratorPayload",
+          "description": "Autogenerated return type of RemoveOutsideCollaborator",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "removedUser",
+              "description": "The user that was removed as an outside collaborator.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "RemoveOutsideCollaboratorInput",
+          "description": "Autogenerated input type of RemoveOutsideCollaborator",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "userId",
+              "description": "The ID of the outside collaborator to remove.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "organizationId",
+              "description": "The ID of the organization to remove the outside collaborator from.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RequestReviewsPayload",
+          "description": "Autogenerated return type of RequestReviews",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "pullRequest",
+              "description": "The pull request that is getting requests.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "PullRequest",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "requestedReviewersEdge",
+              "description": "The edge from the pull request to the requested reviewers.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserEdge",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "RequestReviewsInput",
+          "description": "Autogenerated input type of RequestReviews",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "pullRequestId",
+              "description": "The Node ID of the pull request to modify.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "userIds",
+              "description": "The Node IDs of the user to request.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "teamIds",
+              "description": "The Node IDs of the team to request.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "union",
+              "description": "Add users to the set rather than replace.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AddStarPayload",
+          "description": "Autogenerated return type of AddStar",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "starrable",
+              "description": "The starrable.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Starrable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AddStarInput",
+          "description": "Autogenerated input type of AddStar",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "starrableId",
+              "description": "The Starrable ID to star.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "RemoveStarPayload",
+          "description": "Autogenerated return type of RemoveStar",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "starrable",
+              "description": "The starrable.",
+              "args": [],
+              "type": {
+                "kind": "INTERFACE",
+                "name": "Starrable",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "RemoveStarInput",
+          "description": "Autogenerated input type of RemoveStar",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "starrableId",
+              "description": "The Starrable ID to unstar.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "AcceptTopicSuggestionPayload",
+          "description": "Autogenerated return type of AcceptTopicSuggestion",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "topic",
+              "description": "The accepted topic.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Topic",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "AcceptTopicSuggestionInput",
+          "description": "Autogenerated input type of AcceptTopicSuggestion",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "repositoryId",
+              "description": "The Node ID of the repository.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the suggested topic.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeclineTopicSuggestionPayload",
+          "description": "Autogenerated return type of DeclineTopicSuggestion",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "topic",
+              "description": "The declined topic.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Topic",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeclineTopicSuggestionInput",
+          "description": "Autogenerated input type of DeclineTopicSuggestion",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "repositoryId",
+              "description": "The Node ID of the repository.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "name",
+              "description": "The name of the suggested topic.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "reason",
+              "description": "The reason why the suggested topic is declined.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "TopicSuggestionDeclineReason",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "TopicSuggestionDeclineReason",
+          "description": "Reason that the suggested topic is declined.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "NOT_RELEVANT",
+              "description": "The suggested topic is not relevant to the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "TOO_SPECIFIC",
+              "description": "The suggested topic is too specific for the repository (e.g. #ruby-on-rails-version-4-2-1).",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "PERSONAL_PREFERENCE",
+              "description": "The viewer does not like the suggested topic.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "TOO_GENERAL",
+              "description": "The suggested topic is too general for the repository.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdateTopicsPayload",
+          "description": "Autogenerated return type of UpdateTopics",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "invalidTopicNames",
+              "description": "Names of the provided topics that are not valid.",
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The updated repository.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "Repository",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdateTopicsInput",
+          "description": "Autogenerated input type of UpdateTopics",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "repositoryId",
+              "description": "The Node ID of the repository.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "topicNames",
+              "description": "An array of topic names.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "SCALAR",
+                      "name": "String",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "CreateBranchProtectionRulePayload",
+          "description": "Autogenerated return type of CreateBranchProtectionRule",
+          "fields": [
+            {
+              "name": "branchProtectionRule",
+              "description": "The newly created BranchProtectionRule.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "BranchProtectionRule",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CreateBranchProtectionRuleInput",
+          "description": "Autogenerated input type of CreateBranchProtectionRule",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "repositoryId",
+              "description": "The global relay id of the repository in which a new branch protection rule should be created in.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "pattern",
+              "description": "The glob-like pattern used to determine matching branches.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresApprovingReviews",
+              "description": "Are approving reviews required to update matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiredApprovingReviewCount",
+              "description": "Number of approving reviews required to update matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresCommitSignatures",
+              "description": "Are commits required to be signed.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "isAdminEnforced",
+              "description": "Can admins overwrite branch protection.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresStatusChecks",
+              "description": "Are status checks required to update matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresStrictStatusChecks",
+              "description": "Are branches required to be up to date before merging.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresCodeOwnerReviews",
+              "description": "Are reviews from code owners required to update matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "dismissesStaleReviews",
+              "description": "Will new commits pushed to matching branches dismiss pull request review approvals.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "restrictsReviewDismissals",
+              "description": "Is dismissal of pull request reviews restricted.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "reviewDismissalActorIds",
+              "description": "A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "restrictsPushes",
+              "description": "Is pushing to matching branches restricted.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "pushActorIds",
+              "description": "A list of User or Team IDs allowed to push to matching branches.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiredStatusCheckContexts",
+              "description": "List of required status check contexts that must pass for commits to be accepted to matching branches.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UpdateBranchProtectionRulePayload",
+          "description": "Autogenerated return type of UpdateBranchProtectionRule",
+          "fields": [
+            {
+              "name": "branchProtectionRule",
+              "description": "The newly created BranchProtectionRule.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "BranchProtectionRule",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "UpdateBranchProtectionRuleInput",
+          "description": "Autogenerated input type of UpdateBranchProtectionRule",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "branchProtectionRuleId",
+              "description": "The global relay id of the branch protection rule to be updated.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "pattern",
+              "description": "The glob-like pattern used to determine matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresApprovingReviews",
+              "description": "Are approving reviews required to update matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiredApprovingReviewCount",
+              "description": "Number of approving reviews required to update matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Int",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresCommitSignatures",
+              "description": "Are commits required to be signed.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "isAdminEnforced",
+              "description": "Can admins overwrite branch protection.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresStatusChecks",
+              "description": "Are status checks required to update matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresStrictStatusChecks",
+              "description": "Are branches required to be up to date before merging.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiresCodeOwnerReviews",
+              "description": "Are reviews from code owners required to update matching branches.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "dismissesStaleReviews",
+              "description": "Will new commits pushed to matching branches dismiss pull request review approvals.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "restrictsReviewDismissals",
+              "description": "Is dismissal of pull request reviews restricted.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "reviewDismissalActorIds",
+              "description": "A list of User or Team IDs allowed to dismiss reviews on pull requests targeting matching branches.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "restrictsPushes",
+              "description": "Is pushing to matching branches restricted.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "pushActorIds",
+              "description": "A list of User or Team IDs allowed to push to matching branches.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "ID",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "requiredStatusCheckContexts",
+              "description": "List of required status check contexts that must pass for commits to be accepted to matching branches.",
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "SCALAR",
+                    "name": "String",
+                    "ofType": null
+                  }
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "DeleteBranchProtectionRulePayload",
+          "description": "Autogenerated return type of DeleteBranchProtectionRule",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "DeleteBranchProtectionRuleInput",
+          "description": "Autogenerated input type of DeleteBranchProtectionRule",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "branchProtectionRuleId",
+              "description": "The global relay id of the branch protection rule to be deleted.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ChangeUserStatusPayload",
+          "description": "Autogenerated return type of ChangeUserStatus",
+          "fields": [
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "status",
+              "description": "Your updated status.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "UserStatus",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "ChangeUserStatusInput",
+          "description": "Autogenerated input type of ChangeUserStatus",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "emoji",
+              "description": "The emoji to represent your status. Can either be a native Unicode emoji or an emoji name with colons, e.g., :grinning:.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "message",
+              "description": "A short description of your current status.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "organizationId",
+              "description": "The ID of the organization whose members will be allowed to see the status. If omitted, the status will be publicly visible.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "ID",
+                "ofType": null
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "limitedAvailability",
+              "description": "Whether this status should indicate you are not fully available on GitHub, e.g., you are away.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "Boolean",
+                "ofType": null
+              },
+              "defaultValue": "false"
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ContentAttachment",
+          "description": "A content attachment",
+          "fields": [
+            {
+              "name": "body",
+              "description": "The body text of the content attachment. This parameter supports markdown.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "contentReference",
+              "description": "The content reference that the content attachment is attached to.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "ContentReference",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "title",
+              "description": "The title of the content attachment.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "ContentReference",
+          "description": "A content reference",
+          "fields": [
+            {
+              "name": "databaseId",
+              "description": "Identifies the primary key from the database.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Int",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "reference",
+              "description": "The reference of the content reference.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "INPUT_OBJECT",
+          "name": "CreateContentAttachmentInput",
+          "description": "Autogenerated input type of CreateContentAttachment",
+          "fields": null,
+          "inputFields": [
+            {
+              "name": "contentReferenceId",
+              "description": "The node ID of the content_reference.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "title",
+              "description": "The title of the content attachment.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "body",
+              "description": "The body of the content attachment, which may contain markdown.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            },
+            {
+              "name": "clientMutationId",
+              "description": "A unique identifier for the client performing the mutation.",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": null
+            }
+          ],
+          "interfaces": null,
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "__Schema",
+          "description": "A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.",
+          "fields": [
+            {
+              "name": "directives",
+              "description": "A list of all directives supported by this server.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "__Directive",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "mutationType",
+              "description": "If this server supports mutation, the type that mutation operations will be rooted at.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "__Type",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "queryType",
+              "description": "The type that query operations will be rooted at.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "__Type",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "subscriptionType",
+              "description": "If this server support subscription, the type that subscription operations will be rooted at.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "__Type",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "types",
+              "description": "A list of all types supported by this server.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "__Type",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "__Type",
+          "description": "The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.\n\nDepending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name and description, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.",
+          "fields": [
+            {
+              "name": "description",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "enumValues",
+              "description": null,
+              "args": [
+                {
+                  "name": "includeDeprecated",
+                  "description": null,
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "__EnumValue",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "fields",
+              "description": null,
+              "args": [
+                {
+                  "name": "includeDeprecated",
+                  "description": null,
+                  "type": {
+                    "kind": "SCALAR",
+                    "name": "Boolean",
+                    "ofType": null
+                  },
+                  "defaultValue": "false"
+                }
+              ],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "__Field",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "inputFields",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "__InputValue",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "interfaces",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "__Type",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "kind",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "__TypeKind",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ofType",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "__Type",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "possibleTypes",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "LIST",
+                "name": null,
+                "ofType": {
+                  "kind": "NON_NULL",
+                  "name": null,
+                  "ofType": {
+                    "kind": "OBJECT",
+                    "name": "__Type",
+                    "ofType": null
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "__Field",
+          "description": "Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.",
+          "fields": [
+            {
+              "name": "args",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "__InputValue",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "deprecationReason",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isDeprecated",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "type",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "__Type",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "__Directive",
+          "description": "A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.\n\nIn some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.",
+          "fields": [
+            {
+              "name": "args",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "OBJECT",
+                      "name": "__InputValue",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "locations",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "LIST",
+                  "name": null,
+                  "ofType": {
+                    "kind": "NON_NULL",
+                    "name": null,
+                    "ofType": {
+                      "kind": "ENUM",
+                      "name": "__DirectiveLocation",
+                      "ofType": null
+                    }
+                  }
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "onField",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "Use `locations`."
+            },
+            {
+              "name": "onFragment",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "Use `locations`."
+            },
+            {
+              "name": "onOperation",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": true,
+              "deprecationReason": "Use `locations`."
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "__EnumValue",
+          "description": "One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.",
+          "fields": [
+            {
+              "name": "deprecationReason",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isDeprecated",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "__InputValue",
+          "description": "Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.",
+          "fields": [
+            {
+              "name": "defaultValue",
+              "description": "A GraphQL-formatted string representing the default value for this input value.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "description",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "type",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "__Type",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "__TypeKind",
+          "description": "An enum describing what kind of type a given `__Type` is.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "SCALAR",
+              "description": "Indicates this type is a scalar.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OBJECT",
+              "description": "Indicates this type is an object. `fields` and `interfaces` are valid fields.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INTERFACE",
+              "description": "Indicates this type is an interface. `fields` and `possibleTypes` are valid fields.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNION",
+              "description": "Indicates this type is a union. `possibleTypes` is a valid field.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ENUM",
+              "description": "Indicates this type is an enum. `enumValues` is a valid field.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INPUT_OBJECT",
+              "description": "Indicates this type is an input object. `inputFields` is a valid field.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "LIST",
+              "description": "Indicates this type is a list. `ofType` is a valid field.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "NON_NULL",
+              "description": "Indicates this type is a non-null. `ofType` is a valid field.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "ENUM",
+          "name": "__DirectiveLocation",
+          "description": "A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.",
+          "fields": null,
+          "inputFields": null,
+          "interfaces": null,
+          "enumValues": [
+            {
+              "name": "QUERY",
+              "description": "Location adjacent to a query operation.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "MUTATION",
+              "description": "Location adjacent to a mutation operation.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SUBSCRIPTION",
+              "description": "Location adjacent to a subscription operation.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FIELD",
+              "description": "Location adjacent to a field.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FRAGMENT_DEFINITION",
+              "description": "Location adjacent to a fragment definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FRAGMENT_SPREAD",
+              "description": "Location adjacent to a fragment spread.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INLINE_FRAGMENT",
+              "description": "Location adjacent to an inline fragment.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SCHEMA",
+              "description": "Location adjacent to a schema definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "SCALAR",
+              "description": "Location adjacent to a scalar definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "OBJECT",
+              "description": "Location adjacent to an object type definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "FIELD_DEFINITION",
+              "description": "Location adjacent to a field definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ARGUMENT_DEFINITION",
+              "description": "Location adjacent to an argument definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INTERFACE",
+              "description": "Location adjacent to an interface definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "UNION",
+              "description": "Location adjacent to a union definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ENUM",
+              "description": "Location adjacent to an enum definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "ENUM_VALUE",
+              "description": "Location adjacent to an enum value definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INPUT_OBJECT",
+              "description": "Location adjacent to an input object type definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "INPUT_FIELD_DEFINITION",
+              "description": "Location adjacent to an input object field definition.",
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "GpgSignature",
+          "description": "Represents a GPG signature on a Commit or Tag.",
+          "fields": [
+            {
+              "name": "email",
+              "description": "Email used to sign this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isValid",
+              "description": "True if the signature is valid and verified by GitHub.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "keyId",
+              "description": "Hex-encoded ID of the key that signed this object.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "payload",
+              "description": "Payload for GPG signing object. Raw ODB object without the signature header.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signature",
+              "description": "ASCII-armored signature header from object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signer",
+              "description": "GitHub user corresponding to the email signing this commit.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "GitSignatureState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "wasSignedByGitHub",
+              "description": "True if the signature was made with GitHub's signing key.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "GitSignature",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "SmimeSignature",
+          "description": "Represents an S/MIME signature on a Commit or Tag.",
+          "fields": [
+            {
+              "name": "email",
+              "description": "Email used to sign this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isValid",
+              "description": "True if the signature is valid and verified by GitHub.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "payload",
+              "description": "Payload for GPG signing object. Raw ODB object without the signature header.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signature",
+              "description": "ASCII-armored signature header from object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signer",
+              "description": "GitHub user corresponding to the email signing this commit.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "GitSignatureState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "wasSignedByGitHub",
+              "description": "True if the signature was made with GitHub's signing key.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "GitSignature",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "Tag",
+          "description": "Represents a Git tag.",
+          "fields": [
+            {
+              "name": "abbreviatedOid",
+              "description": "An abbreviated version of the Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitResourcePath",
+              "description": "The HTTP path for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "commitUrl",
+              "description": "The HTTP URL for this Git object",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "URI",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "id",
+              "description": null,
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "ID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "message",
+              "description": "The Git tag message.",
+              "args": [],
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "name",
+              "description": "The Git tag name.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "oid",
+              "description": "The Git object ID",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "GitObjectID",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "repository",
+              "description": "The Repository the Git object belongs to",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "OBJECT",
+                  "name": "Repository",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "tagger",
+              "description": "Details about the tag author.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "GitActor",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "target",
+              "description": "The Git object the tag points to.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "INTERFACE",
+                  "name": "GitObject",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "Node",
+              "ofType": null
+            },
+            {
+              "kind": "INTERFACE",
+              "name": "GitObject",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        },
+        {
+          "kind": "OBJECT",
+          "name": "UnknownSignature",
+          "description": "Represents an unknown signature on a Commit or Tag.",
+          "fields": [
+            {
+              "name": "email",
+              "description": "Email used to sign this object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "isValid",
+              "description": "True if the signature is valid and verified by GitHub.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "payload",
+              "description": "Payload for GPG signing object. Raw ODB object without the signature header.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signature",
+              "description": "ASCII-armored signature header from object.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "String",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "signer",
+              "description": "GitHub user corresponding to the email signing this commit.",
+              "args": [],
+              "type": {
+                "kind": "OBJECT",
+                "name": "User",
+                "ofType": null
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "state",
+              "description": "The state of this signature. `VALID` if signature is valid and verified by GitHub, otherwise represents reason why signature is considered invalid.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "ENUM",
+                  "name": "GitSignatureState",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            },
+            {
+              "name": "wasSignedByGitHub",
+              "description": "True if the signature was made with GitHub's signing key.",
+              "args": [],
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "isDeprecated": false,
+              "deprecationReason": null
+            }
+          ],
+          "inputFields": null,
+          "interfaces": [
+            {
+              "kind": "INTERFACE",
+              "name": "GitSignature",
+              "ofType": null
+            }
+          ],
+          "enumValues": null,
+          "possibleTypes": null
+        }
+      ],
+      "directives": [
+        {
+          "name": "include",
+          "description": "Directs the executor to include this field or fragment only when the `if` argument is true.",
+          "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
+          "args": [
+            {
+              "name": "if",
+              "description": "Included when true.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ]
+        },
+        {
+          "name": "skip",
+          "description": "Directs the executor to skip this field or fragment when the `if` argument is true.",
+          "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
+          "args": [
+            {
+              "name": "if",
+              "description": "Skipped when true.",
+              "type": {
+                "kind": "NON_NULL",
+                "name": null,
+                "ofType": {
+                  "kind": "SCALAR",
+                  "name": "Boolean",
+                  "ofType": null
+                }
+              },
+              "defaultValue": null
+            }
+          ]
+        },
+        {
+          "name": "deprecated",
+          "description": "Marks an element of a GraphQL schema as no longer supported.",
+          "locations": ["FIELD_DEFINITION", "ENUM_VALUE"],
+          "args": [
+            {
+              "name": "reason",
+              "description": "Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted in [Markdown](https://daringfireball.net/projects/markdown/).",
+              "type": {
+                "kind": "SCALAR",
+                "name": "String",
+                "ofType": null
+              },
+              "defaultValue": "\"No longer supported\""
+            }
+          ]
+        }
+      ]
+    }
+  }
+}
diff --git a/tests/fixtures/kitchen_sink.graphql b/tests/fixtures/kitchen_sink.graphql
new file mode 100644
index 0000000..874fdc1
--- /dev/null
+++ b/tests/fixtures/kitchen_sink.graphql
@@ -0,0 +1,58 @@
+query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery {
+  whoever123is: node(id: [123, 456]) {
+    id ,
+    ... on User @onInlineFragment {
+      field2 {
+        id ,
+        alias: field1(first:10, after:$foo,) @include(if: $foo) {
+          id,
+          ...frag @onFragmentSpread
+        }
+      }
+    }
+    ... @skip(unless: $foo) {
+      id
+    }
+    ... {
+      id
+    }
+  }
+}
+
+mutation likeStory @onMutation {
+  like(story: 123) @onField {
+    story {
+      id @onField
+    }
+  }
+}
+
+subscription StoryLikeSubscription(
+  $input: StoryLikeSubscribeInput
+) @onSubscription {
+  storyLikeSubscribe(input: $input) {
+    story {
+      likers {
+        count
+      }
+      likeSentence {
+        text
+      }
+    }
+  }
+}
+
+fragment frag on Friend @onFragmentDefinition {
+  foo(size: $size, bar: $b, obj: {key: "value", block: """
+
+      block string uses \"""
+
+  """})
+}
+
+{
+  unnamed(truthy: true, falsy: false, nullish: null),
+  query
+}
+
+query { __typename }
diff --git a/tests/fixtures/schema_kitchen_sink.graphql b/tests/fixtures/schema_kitchen_sink.graphql
new file mode 100644
index 0000000..8ec1f2d
--- /dev/null
+++ b/tests/fixtures/schema_kitchen_sink.graphql
@@ -0,0 +1,156 @@
+"""This is a description of the schema as a whole."""
+schema {
+  query: QueryType
+  mutation: MutationType
+}
+
+"""
+This is a description
+of the `Foo` type.
+"""
+type Foo implements Bar & Baz & Two {
+  "Description of the `one` field."
+  one: Type
+  """
+  This is a description of the `two` field.
+  """
+  two(
+    """
+    This is a description of the `argument` argument.
+    """
+    argument: InputType!
+  ): Type
+  """This is a description of the `three` field."""
+  three(argument: InputType, other: String): Int
+  four(argument: String = "string"): String
+  five(argument: [String] = ["string", "string"]): String
+  six(argument: InputType = {key: "value"}): Type
+  seven(argument: Int = null): Type
+}
+
+type AnnotatedObject @onObject(arg: "value") {
+  annotatedField(arg: Type = "default" @onArgumentDefinition): Type @onField
+}
+
+type UndefinedType
+
+extend type Foo {
+  seven(argument: [String]): Type
+}
+
+extend type Foo @onType
+
+interface Bar {
+  one: Type
+  four(argument: String = "string"): String
+}
+
+interface AnnotatedInterface @onInterface {
+  annotatedField(arg: Type @onArgumentDefinition): Type @onField
+}
+
+interface UndefinedInterface
+
+extend interface Bar implements Two {
+  two(argument: InputType!): Type
+}
+
+extend interface Bar @onInterface
+
+interface Baz implements Bar & Two {
+  one: Type
+  two(argument: InputType!): Type
+  four(argument: String = "string"): String
+}
+
+union Feed =
+  | Story
+  | Article
+  | Advert
+
+union AnnotatedUnion @onUnion = A | B
+
+union AnnotatedUnionTwo @onUnion = | A | B
+
+union UndefinedUnion
+
+extend union Feed = Photo | Video
+
+extend union Feed @onUnion
+
+scalar CustomScalar
+
+scalar AnnotatedScalar @onScalar
+
+extend scalar CustomScalar @onScalar
+
+enum Site {
+  """
+  This is a description of the `DESKTOP` value
+  """
+  DESKTOP
+
+  """This is a description of the `MOBILE` value"""
+  MOBILE
+
+  "This is a description of the `WEB` value"
+  WEB
+}
+
+enum AnnotatedEnum @onEnum {
+  ANNOTATED_VALUE @onEnumValue
+  OTHER_VALUE
+}
+
+enum UndefinedEnum
+
+extend enum Site {
+  VR
+}
+
+extend enum Site @onEnum
+
+input InputType {
+  key: String!
+  answer: Int = 42
+}
+
+input AnnotatedInput @onInputObject {
+  annotatedField: Type @onInputFieldDefinition
+}
+
+input UndefinedInput
+
+extend input InputType {
+  other: Float = 1.23e4 @onInputFieldDefinition
+}
+
+extend input InputType @onInputObject
+
+"""
+This is a description of the `@skip` directive
+"""
+directive @skip(
+  """This is a description of the `if` argument"""
+  if: Boolean! @onArgumentDefinition
+) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+directive @include(if: Boolean!)
+  on FIELD
+   | FRAGMENT_SPREAD
+   | INLINE_FRAGMENT
+
+directive @include2(if: Boolean!) on
+  | FIELD
+  | FRAGMENT_SPREAD
+  | INLINE_FRAGMENT
+
+directive @myRepeatableDir(name: String!) repeatable on
+  | OBJECT
+  | INTERFACE
+
+extend schema @onSchema
+
+extend schema @onSchema {
+  subscription: SubscriptionType
+}
diff --git a/tests/language/__init__.py b/tests/language/__init__.py
new file mode 100644
index 0000000..f58e065
--- /dev/null
+++ b/tests/language/__init__.py
@@ -0,0 +1 @@
+"""Tests for graphql.language"""
diff --git a/tests/language/test_ast.py b/tests/language/test_ast.py
new file mode 100644
index 0000000..39d8aed
--- /dev/null
+++ b/tests/language/test_ast.py
@@ -0,0 +1,237 @@
+from copy import copy, deepcopy
+import weakref
+
+from graphql.language import Location, Node, Source, Token, TokenKind
+from graphql.pyutils import inspect
+
+
+class SampleTestNode(Node):
+    __slots__ = "alpha", "beta"
+
+    alpha: int
+    beta: int
+
+
+def describe_token_class():
+    def initializes():
+        prev = Token(TokenKind.EQUALS, 10, 11, 1, 2)
+        token = Token(
+            kind=TokenKind.NAME,
+            start=11,
+            end=12,
+            line=1,
+            column=3,
+            prev=prev,
+            value="n",
+        )
+        assert prev.kind == TokenKind.EQUALS
+        assert prev.start == 10
+        assert prev.end == 11
+        assert prev.line == 1
+        assert prev.column == 2
+        assert token.kind == TokenKind.NAME
+        assert token.start == 11
+        assert token.end == 12
+        assert token.line == 1
+        assert token.column == 3
+        assert token.prev is prev
+        assert token.value == "n"
+
+    def can_stringify():
+        token = Token(TokenKind.NAME, 1, 2, 1, 2, value="test")
+        assert str(token) == "Name 'test'"
+        assert token.desc == str(token)
+
+    def has_representation_with_line_and_column():
+        token = Token(TokenKind.NAME, 1, 2, 1, 2, value="test")
+        assert repr(token) == "<Token Name 'test' 1:2>"
+        assert inspect(token) == repr(token)
+
+    def can_check_equality():
+        token1 = Token(TokenKind.NAME, 1, 2, 1, 2, value="test")
+        token2 = Token(TokenKind.NAME, 1, 2, 1, 2, value="test")
+        assert token2 == token1
+        assert not token2 != token1
+        token3 = Token(TokenKind.NAME, 1, 2, 1, 2, value="text")
+        assert token3 != token1
+        token4 = Token(TokenKind.NAME, 1, 4, 1, 2, value="test")
+        assert token4 != token1
+        token5 = Token(TokenKind.NAME, 1, 2, 1, 4, value="test")
+        assert token5 != token1
+
+    def can_compare_with_string():
+        token = Token(TokenKind.NAME, 1, 2, 1, 2, value="test")
+        assert token == "Name 'test'"
+        assert token != "Name 'foo'"
+
+    def does_not_equal_incompatible_object():
+        token = Token(TokenKind.NAME, 1, 2, 1, 2, value="test")
+        assert token != {"Name": "test"}
+
+    def can_hash():
+        token1 = Token(TokenKind.NAME, 1, 2, 1, 2, value="hash")
+        token2 = Token(TokenKind.NAME, 1, 2, 1, 2, value="hash")
+        assert token2 == token1
+        assert hash(token2) == hash(token1)
+        token3 = Token(TokenKind.NAME, 1, 2, 1, 2, value="bash")
+        assert token3 != token1
+        assert hash(token3) != hash(token1)
+
+    def can_copy():
+        token1 = Token(TokenKind.NAME, 1, 2, 1, 2, value="copy")
+        token2 = copy(token1)
+        assert token2 == token1
+        assert token2 is not token1
+
+
+def describe_location_class():
+    token1 = Token(TokenKind.NAME, 1, 2, 1, 2)
+    token2 = Token(TokenKind.NAME, 2, 3, 1, 3)
+    source = Source("source")
+
+    def initializes():
+        loc = Location(token1, token2, source)
+        assert loc.start == token1.start
+        assert loc.end == token2.end
+        assert loc.start_token is token1
+        assert loc.end_token is token2
+        assert loc.source is source
+
+    def can_stringify_with_start_and_end():
+        loc = Location(token1, token2, source)
+        assert str(loc) == "1:3"
+
+    def has_representation_with_start_and_end():
+        loc = Location(token1, token2, source)
+        assert repr(loc) == "<Location 1:3>"
+        assert inspect(loc) == repr(loc)
+
+    def can_check_equality():
+        loc1 = Location(token1, token2, source)
+        loc2 = Location(token1, token2, source)
+        assert loc2 == loc1
+        loc3 = Location(token1, token1, source)
+        assert loc3 != loc1
+        loc4 = Location(token2, token2, source)
+        assert loc4 != loc1
+        assert loc4 != loc3
+
+    def can_check_equality_with_tuple_or_list():
+        loc = Location(token1, token2, source)
+        assert loc == (1, 3)
+        assert loc == [1, 3]
+        assert not loc != (1, 3)
+        assert not loc != [1, 3]
+        assert loc != (1, 2)
+        assert loc != [2, 3]
+
+    def does_not_equal_incompatible_object():
+        loc = Location(token1, token2, source)
+        assert not loc == (1, 2, 3)
+        assert loc != (1, 2, 3)
+        assert not loc == {1: 2}
+        assert loc != {1: 2}
+
+    def can_hash():
+        loc1 = Location(token1, token2, source)
+        loc2 = Location(token1, token2, source)
+        assert loc2 == loc1
+        assert hash(loc2) == hash(loc1)
+        loc3 = Location(token1, token1, source)
+        assert loc3 != loc1
+        assert hash(loc3) != hash(loc1)
+        loc4 = Location(token2, token2, source)
+        assert loc4 != loc1
+        assert hash(loc4) != hash(loc1)
+        assert hash(loc4) != hash(loc3)
+
+
+def describe_node_class():
+    def initializes_with_keywords():
+        node = SampleTestNode(alpha=1, beta=2, loc=0)
+        assert node.alpha == 1
+        assert node.beta == 2
+        assert node.loc == 0
+        node = SampleTestNode(alpha=1, loc=None)
+        assert node.loc is None
+        assert node.alpha == 1
+        assert node.beta is None
+        node = SampleTestNode(alpha=1, beta=2, gamma=3)
+        assert node.alpha == 1
+        assert node.beta == 2
+        assert not hasattr(node, "gamma")
+
+    def has_representation_with_loc():
+        node = SampleTestNode(alpha=1, beta=2)
+        assert repr(node) == "SampleTestNode"
+        node = SampleTestNode(alpha=1, beta=2, loc=3)
+        assert repr(node) == "SampleTestNode at 3"
+
+    def can_check_equality():
+        node = SampleTestNode(alpha=1, beta=2)
+        node2 = SampleTestNode(alpha=1, beta=2)
+        assert node2 == node
+        assert not node2 != node
+        node2 = SampleTestNode(alpha=1, beta=1)
+        assert node2 != node
+        node3 = Node(alpha=1, beta=2)
+        assert node3 != node
+
+    def can_hash():
+        node = SampleTestNode(alpha=1, beta=2)
+        node2 = SampleTestNode(alpha=1, beta=2)
+        assert node == node2
+        assert node2 is not node
+        assert hash(node2) == hash(node)
+        node3 = SampleTestNode(alpha=1, beta=3)
+        assert node3 != node
+        assert hash(node3) != hash(node)
+
+    def can_create_weak_reference():
+        node = SampleTestNode(alpha=1, beta=2)
+        ref = weakref.ref(node)
+        assert ref() is node
+
+    def can_create_custom_attribute():
+        node = SampleTestNode(alpha=1, beta=2)
+        node.gamma = 3  # type: ignore
+        assert node.gamma == 3  # type: ignore
+
+    def can_create_shallow_copy():
+        node = SampleTestNode(alpha=1, beta=2)
+        node2 = copy(node)
+        assert node2 is not node
+        assert node2 == node
+
+    def shallow_copy_is_really_shallow():
+        node = SampleTestNode(alpha=1, beta=2)
+        node2 = SampleTestNode(alpha=node, beta=node)
+        node3 = copy(node2)
+        assert node3 is not node2
+        assert node3 == node2
+        assert node3.alpha is node2.alpha
+        assert node3.beta is node2.beta
+
+    def can_create_deep_copy():
+        alpha = SampleTestNode(alpha=1, beta=2)
+        beta = SampleTestNode(alpha=3, beta=4)
+        node = SampleTestNode(alpha=alpha, beta=beta)
+        node2 = deepcopy(node)
+        assert node2 is not node
+        assert node2 == node
+        assert node2.alpha == alpha
+        assert node2.alpha is not alpha
+        assert node2.alpha == alpha
+        assert node2.beta is not beta
+
+    def provides_snake_cased_kind_as_class_attribute():
+        assert SampleTestNode.kind == "sample_test"
+
+    def provides_proper_kind_if_class_does_not_end_with_node():
+        class Foo(Node):
+            pass
+
+        assert Foo.kind == "foo"
+
+    def provides_keys_as_class_attribute():
+        assert SampleTestNode.keys == ["loc", "alpha", "beta"]
diff --git a/tests/language/test_block_string.py b/tests/language/test_block_string.py
new file mode 100644
index 0000000..28605bf
--- /dev/null
+++ b/tests/language/test_block_string.py
@@ -0,0 +1,134 @@
+from graphql.language.block_string import (
+    dedent_block_string_value,
+    print_block_string,
+    get_block_string_indentation,
+)
+
+
+def join_lines(*args):
+    return "\n".join(args)
+
+
+def describe_dedent_block_string_value():
+    def removes_uniform_indentation_from_a_string():
+        raw_value = join_lines(
+            "", "    Hello,", "      World!", "", "    Yours,", "      GraphQL."
+        )
+        assert dedent_block_string_value(raw_value) == join_lines(
+            "Hello,", "  World!", "", "Yours,", "  GraphQL."
+        )
+
+    def removes_empty_leading_and_trailing_lines():
+        raw_value = join_lines(
+            "",
+            "",
+            "    Hello,",
+            "      World!",
+            "",
+            "    Yours,",
+            "      GraphQL.",
+            "",
+            "",
+        )
+        assert dedent_block_string_value(raw_value) == join_lines(
+            "Hello,", "  World!", "", "Yours,", "  GraphQL."
+        )
+
+    def removes_blank_leading_and_trailing_lines():
+        raw_value = join_lines(
+            "  ",
+            "        ",
+            "    Hello,",
+            "      World!",
+            "",
+            "    Yours,",
+            "      GraphQL.",
+            "        ",
+            "  ",
+        )
+        assert dedent_block_string_value(raw_value) == join_lines(
+            "Hello,", "  World!", "", "Yours,", "  GraphQL."
+        )
+
+    def retains_indentation_from_first_line():
+        raw_value = join_lines(
+            "    Hello,", "      World!", "", "    Yours,", "      GraphQL."
+        )
+        assert dedent_block_string_value(raw_value) == join_lines(
+            "    Hello,", "  World!", "", "Yours,", "  GraphQL."
+        )
+
+    def does_not_alter_trailing_spaces():
+        raw_value = join_lines(
+            "               ",
+            "    Hello,     ",
+            "      World!   ",
+            "               ",
+            "    Yours,     ",
+            "      GraphQL. ",
+            "               ",
+        )
+        assert dedent_block_string_value(raw_value) == join_lines(
+            "Hello,     ", "  World!   ", "           ", "Yours,     ", "  GraphQL. "
+        )
+
+
+def describe_get_block_string_indentation():
+    def returns_zero_for_an_empty_list():
+        assert get_block_string_indentation([]) == 0
+
+    def do_not_take_first_line_into_account():
+        assert get_block_string_indentation(["  a"]) == 0
+        assert get_block_string_indentation([" a", "  b"]) == 2
+
+    def returns_minimal_indentation_length():
+        assert get_block_string_indentation(["", " a", "  b"]) == 1
+        assert get_block_string_indentation(["", "  a", " b"]) == 1
+        assert get_block_string_indentation(["", "  a", " b", "c"]) == 0
+
+    def count_both_tab_and_space_as_single_character():
+        assert get_block_string_indentation(["", "\ta", "          b"]) == 1
+        assert get_block_string_indentation(["", "\t a", "          b"]) == 2
+        assert get_block_string_indentation(["", " \t a", "          b"]) == 3
+
+    def do_not_take_empty_lines_into_account():
+        assert get_block_string_indentation((["a", "\t"])) == 0
+        assert get_block_string_indentation((["a", " "])) == 0
+        assert get_block_string_indentation((["a", " ", "  b"])) == 2
+        assert get_block_string_indentation((["a", " ", "  b"])) == 2
+        assert get_block_string_indentation((["a", "", " b"])) == 1
+
+
+def describe_print_block_string():
+    def by_default_print_block_strings_as_single_line():
+        s = "one liner"
+        assert print_block_string(s) == '"""one liner"""'
+        assert print_block_string(s, "", True) == '"""\none liner\n"""'
+
+    def correctly_prints_single_line_with_leading_space():
+        s = "    space-led string"
+        assert print_block_string(s) == '"""    space-led string"""'
+        assert print_block_string(s, "", True) == '"""    space-led string\n"""'
+
+    def correctly_prints_single_line_with_leading_space_and_quotation():
+        s = '    space-led value "quoted string"'
+
+        assert print_block_string(s) == '"""    space-led value "quoted string"\n"""'
+
+        assert (
+            print_block_string(s, "", True)
+            == '"""    space-led value "quoted string"\n"""'
+        )
+
+    def correctly_prints_single_line_with_trailing_backslash():
+        s = "backslash \\"
+
+        assert print_block_string(s) == '"""\nbackslash \\\n"""'
+        assert print_block_string(s, "", True) == '"""\nbackslash \\\n"""'
+
+    def correctly_prints_string_with_a_first_line_indentation():
+        s = join_lines("    first  ", "  line     ", "indentation", "     string")
+
+        assert print_block_string(s) == join_lines(
+            '"""', "    first  ", "  line     ", "indentation", "     string", '"""'
+        )
diff --git a/tests/language/test_block_string_fuzz.py b/tests/language/test_block_string_fuzz.py
new file mode 100644
index 0000000..8fb3390
--- /dev/null
+++ b/tests/language/test_block_string_fuzz.py
@@ -0,0 +1,54 @@
+from typing import Optional
+
+from pytest import mark  # type: ignore
+
+from graphql.error import GraphQLSyntaxError
+from graphql.language import Source, Lexer, TokenKind
+from graphql.language.block_string import print_block_string
+
+from ..utils import dedent, gen_fuzz_strings
+
+
+def lex_value(s: str) -> Optional[str]:
+    lexer = Lexer(Source(s))
+    value = lexer.advance().value
+    assert lexer.advance().kind == TokenKind.EOF, "Expected EOF"
+    return value
+
+
+def describe_print_block_string():
+    @mark.slow
+    @mark.timeout(20)
+    def correctly_print_random_strings():
+        # Testing with length >7 is taking exponentially more time. However it is
+        # highly recommended to test with increased limit if you make any change.
+        for fuzz_str in gen_fuzz_strings(allowed_chars='\n\t "a\\', max_length=7):
+            test_str = f'"""{fuzz_str}"""'
+
+            try:
+                test_value = lex_value(test_str)
+            except (AssertionError, GraphQLSyntaxError):
+                continue  # skip invalid values
+            assert isinstance(test_value, str)
+
+            printed_value = lex_value(print_block_string(test_value))
+
+            assert test_value == printed_value, dedent(
+                f"""
+                Expected lex_value(print_block_string({test_value!r})
+                  to equal {test_value!r}
+                  but got {printed_value!r}
+                """
+            )
+
+            printed_multiline_string = lex_value(
+                print_block_string(test_value, " ", True)
+            )
+
+            assert test_value == printed_multiline_string, dedent(
+                f"""
+                Expected lex_value(print_block_string({test_value!r}, ' ', True)
+                  to equal {test_value!r}
+                  but got {printed_multiline_string!r}
+                """
+            )
diff --git a/tests/language/test_lexer.py b/tests/language/test_lexer.py
new file mode 100644
index 0000000..5317b84
--- /dev/null
+++ b/tests/language/test_lexer.py
@@ -0,0 +1,468 @@
+from typing import List, Optional
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLSyntaxError
+from graphql.language import Lexer, Source, SourceLocation, Token, TokenKind
+from graphql.language.lexer import is_punctuator_token_kind
+from graphql.pyutils import inspect
+
+from ..utils import dedent
+
+
+def lex_one(s: str) -> Token:
+    lexer = Lexer(Source(s))
+    return lexer.advance()
+
+
+def lex_second(s: str) -> Token:
+    lexer = Lexer(Source(s))
+    lexer.advance()
+    return lexer.advance()
+
+
+def assert_syntax_error(text, message, location):
+    with raises(GraphQLSyntaxError) as exc_info:
+        lex_second(text)
+    error = exc_info.value
+    assert error.message == f"Syntax Error: {message}"
+    assert error.description == message
+    assert error.locations == [location]
+
+
+def describe_lexer():
+    def disallows_uncommon_control_characters():
+        assert_syntax_error(
+            "\x07", "Cannot contain the invalid character '\\x07'.", (1, 1)
+        )
+
+    def accepts_bom_header():
+        token = lex_one("\uFEFF foo")
+        assert token == Token(TokenKind.NAME, 2, 5, 1, 3, None, "foo")
+
+    def tracks_line_breaks():
+        assert lex_one("foo") == Token(TokenKind.NAME, 0, 3, 1, 1, None, "foo")
+        assert lex_one("\nfoo") == Token(TokenKind.NAME, 1, 4, 2, 1, None, "foo")
+        assert lex_one("\rfoo") == Token(TokenKind.NAME, 1, 4, 2, 1, None, "foo")
+        assert lex_one("\r\nfoo") == Token(TokenKind.NAME, 2, 5, 2, 1, None, "foo")
+        assert lex_one("\n\rfoo") == Token(TokenKind.NAME, 2, 5, 3, 1, None, "foo")
+        assert lex_one("\r\r\n\nfoo") == Token(TokenKind.NAME, 4, 7, 4, 1, None, "foo")
+        assert lex_one("\n\n\r\rfoo") == Token(TokenKind.NAME, 4, 7, 5, 1, None, "foo")
+
+    def records_line_and_column():
+        token = lex_one("\n \r\n \r  foo\n")
+        assert token == Token(TokenKind.NAME, 8, 11, 4, 3, None, "foo")
+
+    def can_be_stringified_or_pyutils_inspected():
+        token = lex_one("foo")
+        assert token.desc == "Name 'foo'"
+        assert str(token) == token.desc
+        assert repr(token) == "<Token Name 'foo' 1:1>"
+        assert inspect(token) == repr(token)
+
+    def skips_whitespace_and_comments():
+        token = lex_one("\n\n    foo\n\n\n")
+        assert token == Token(TokenKind.NAME, 6, 9, 3, 5, None, "foo")
+        token = lex_one("\r\n\r\n  foo\r\n\r\n")
+        assert token == Token(TokenKind.NAME, 6, 9, 3, 3, None, "foo")
+        token = lex_one("\r\r  foo\r\r")
+        assert token == Token(TokenKind.NAME, 4, 7, 3, 3, None, "foo")
+        token = lex_one("\t\t  foo\t\t")
+        assert token == Token(TokenKind.NAME, 4, 7, 1, 5, None, "foo")
+        token = lex_one("\n    #comment\n    foo#comment\n")
+        assert token == Token(TokenKind.NAME, 18, 21, 3, 5, None, "foo")
+        token = lex_one(",,,foo,,,")
+        assert token == Token(TokenKind.NAME, 3, 6, 1, 4, None, "foo")
+
+    def errors_respect_whitespace():
+        with raises(GraphQLSyntaxError) as exc_info:
+            lex_one("\n\n    ?\n")
+
+        assert str(exc_info.value) + "\n" == dedent(
+            """
+            Syntax Error: Cannot parse the unexpected character '?'.
+
+            GraphQL request:3:5
+            2 |
+            3 |     ?
+              |     ^
+            4 |
+            """
+        )
+
+    def updates_line_numbers_in_error_for_file_context():
+        s = "\n\n     ?\n\n"
+        source = Source(s, "foo.js", SourceLocation(11, 12))
+        with raises(GraphQLSyntaxError) as exc_info:
+            Lexer(source).advance()
+        assert str(exc_info.value) + "\n" == dedent(
+            """
+            Syntax Error: Cannot parse the unexpected character '?'.
+
+            foo.js:13:6
+            12 |
+            13 |      ?
+               |      ^
+            14 |
+            """
+        )
+
+    def updates_column_numbers_in_error_for_file_context():
+        source = Source("?", "foo.js", SourceLocation(1, 5))
+        with raises(GraphQLSyntaxError) as exc_info:
+            Lexer(source).advance()
+        assert str(exc_info.value) + "\n" == dedent(
+            """
+            Syntax Error: Cannot parse the unexpected character '?'.
+
+            foo.js:1:5
+            1 |     ?
+              |     ^
+            """
+        )
+
+    # noinspection PyArgumentEqualDefault
+    def lexes_empty_string():
+        token = lex_one('""')
+        assert token == Token(TokenKind.STRING, 0, 2, 1, 1, None, "")
+        assert token.value == ""
+
+    # noinspection PyArgumentEqualDefault
+    def lexes_strings():
+        assert lex_one('""') == Token(TokenKind.STRING, 0, 2, 1, 1, None, "")
+        assert lex_one('"simple"') == Token(
+            TokenKind.STRING, 0, 8, 1, 1, None, "simple"
+        )
+        assert lex_one('" white space "') == Token(
+            TokenKind.STRING, 0, 15, 1, 1, None, " white space "
+        )
+        assert lex_one('"quote \\""') == Token(
+            TokenKind.STRING, 0, 10, 1, 1, None, 'quote "'
+        )
+        assert lex_one('"escaped \\n\\r\\b\\t\\f"') == Token(
+            TokenKind.STRING, 0, 20, 1, 1, None, "escaped \n\r\b\t\f"
+        )
+        assert lex_one('"slashes \\\\ \\/"') == Token(
+            TokenKind.STRING, 0, 15, 1, 1, None, "slashes \\ /"
+        )
+        assert lex_one('"unicode \\u1234\\u5678\\u90AB\\uCDEF"') == Token(
+            TokenKind.STRING, 0, 34, 1, 1, None, "unicode \u1234\u5678\u90AB\uCDEF"
+        )
+
+    def lex_reports_useful_string_errors():
+        assert_syntax_error('"', "Unterminated string.", (1, 2))
+        assert_syntax_error('"""', "Unterminated string.", (1, 4))
+        assert_syntax_error('""""', "Unterminated string.", (1, 5))
+        assert_syntax_error('"no end quote', "Unterminated string.", (1, 14))
+        assert_syntax_error(
+            "'single quotes'",
+            "Unexpected single quote character ('), "
+            'did you mean to use a double quote (")?',
+            (1, 1),
+        )
+        assert_syntax_error(
+            '"contains unescaped \x07 control char"',
+            "Invalid character within String: '\\x07'.",
+            (1, 21),
+        )
+        assert_syntax_error(
+            '"null-byte is not \x00 end of file"',
+            "Invalid character within String: '\\x00'.",
+            (1, 19),
+        )
+        assert_syntax_error('"multi\nline"', "Unterminated string.", (1, 7))
+        assert_syntax_error('"multi\rline"', "Unterminated string.", (1, 7))
+        assert_syntax_error(
+            '"bad \\x esc"', "Invalid character escape sequence: '\\x'.", (1, 7)
+        )
+        assert_syntax_error(
+            '"bad \\u1 esc"', "Invalid character escape sequence: '\\u1 es'.", (1, 7)
+        )
+        assert_syntax_error(
+            '"bad \\u0XX1 esc"', "Invalid character escape sequence: '\\u0XX1'.", (1, 7)
+        )
+        assert_syntax_error(
+            '"bad \\uXXXX esc"', "Invalid character escape sequence: '\\uXXXX'.", (1, 7)
+        )
+        assert_syntax_error(
+            '"bad \\uFXXX esc"', "Invalid character escape sequence: '\\uFXXX'.", (1, 7)
+        )
+        assert_syntax_error(
+            '"bad \\uXXXF esc"', "Invalid character escape sequence: '\\uXXXF'.", (1, 7)
+        )
+
+    # noinspection PyArgumentEqualDefault
+    def lexes_block_strings():
+        assert lex_one('""""""') == Token(TokenKind.BLOCK_STRING, 0, 6, 1, 1, None, "")
+        assert lex_one('"""simple"""') == Token(
+            TokenKind.BLOCK_STRING, 0, 12, 1, 1, None, "simple"
+        )
+        assert lex_one('""" white space """') == Token(
+            TokenKind.BLOCK_STRING, 0, 19, 1, 1, None, " white space "
+        )
+        assert lex_one('"""contains " quote"""') == Token(
+            TokenKind.BLOCK_STRING, 0, 22, 1, 1, None, 'contains " quote'
+        )
+        assert lex_one('"""contains \\""" triple-quote"""') == Token(
+            TokenKind.BLOCK_STRING, 0, 32, 1, 1, None, 'contains """ triple-quote'
+        )
+        assert lex_one('"""multi\nline"""') == Token(
+            TokenKind.BLOCK_STRING, 0, 16, 1, 1, None, "multi\nline"
+        )
+        assert lex_one('"""multi\rline\r\nnormalized"""') == Token(
+            TokenKind.BLOCK_STRING, 0, 28, 1, 1, None, "multi\nline\nnormalized"
+        )
+        assert lex_one('"""unescaped \\n\\r\\b\\t\\f\\u1234"""') == Token(
+            TokenKind.BLOCK_STRING,
+            0,
+            32,
+            1,
+            1,
+            None,
+            "unescaped \\n\\r\\b\\t\\f\\u1234",
+        )
+        assert lex_one('"""slashes \\\\ \\/"""') == Token(
+            TokenKind.BLOCK_STRING, 0, 19, 1, 1, None, "slashes \\\\ \\/"
+        )
+        assert lex_one(
+            '"""\n\n        spans\n          multiple\n'
+            '            lines\n\n        """'
+        ) == Token(
+            TokenKind.BLOCK_STRING, 0, 68, 1, 1, None, "spans\n  multiple\n    lines"
+        )
+
+    def advance_line_after_lexing_multiline_block_string():
+        assert (
+            lex_second(
+                '''"""
+
+        spans
+          multiple
+            lines
+
+        \n """ second_token'''
+            )
+            == Token(TokenKind.NAME, 71, 83, 8, 6, None, "second_token")
+        )
+
+    def lex_reports_useful_block_string_errors():
+        assert_syntax_error('"""', "Unterminated string.", (1, 4))
+        assert_syntax_error('"""no end quote', "Unterminated string.", (1, 16))
+        assert_syntax_error(
+            '"""contains unescaped \x07 control char"""',
+            "Invalid character within String: '\\x07'.",
+            (1, 23),
+        )
+        assert_syntax_error(
+            '"""null-byte is not \x00 end of file"""',
+            "Invalid character within String: '\\x00'.",
+            (1, 21),
+        )
+
+    # noinspection PyArgumentEqualDefault
+    def lexes_numbers():
+        assert lex_one("0") == Token(TokenKind.INT, 0, 1, 1, 1, None, "0")
+        assert lex_one("1") == Token(TokenKind.INT, 0, 1, 1, 1, None, "1")
+        assert lex_one("4") == Token(TokenKind.INT, 0, 1, 1, 1, None, "4")
+        assert lex_one("9") == Token(TokenKind.INT, 0, 1, 1, 1, None, "9")
+        assert lex_one("42") == Token(TokenKind.INT, 0, 2, 1, 1, None, "42")
+        assert lex_one("4.123") == Token(TokenKind.FLOAT, 0, 5, 1, 1, None, "4.123")
+        assert lex_one("-4") == Token(TokenKind.INT, 0, 2, 1, 1, None, "-4")
+        assert lex_one("-42") == Token(TokenKind.INT, 0, 3, 1, 1, None, "-42")
+        assert lex_one("-4.123") == Token(TokenKind.FLOAT, 0, 6, 1, 1, None, "-4.123")
+        assert lex_one("0.123") == Token(TokenKind.FLOAT, 0, 5, 1, 1, None, "0.123")
+        assert lex_one("123e4") == Token(TokenKind.FLOAT, 0, 5, 1, 1, None, "123e4")
+        assert lex_one("123E4") == Token(TokenKind.FLOAT, 0, 5, 1, 1, None, "123E4")
+        assert lex_one("123e-4") == Token(TokenKind.FLOAT, 0, 6, 1, 1, None, "123e-4")
+        assert lex_one("123e+4") == Token(TokenKind.FLOAT, 0, 6, 1, 1, None, "123e+4")
+        assert lex_one("-1.123e4") == Token(
+            TokenKind.FLOAT, 0, 8, 1, 1, None, "-1.123e4"
+        )
+        assert lex_one("-1.123E4") == Token(
+            TokenKind.FLOAT, 0, 8, 1, 1, None, "-1.123E4"
+        )
+        assert lex_one("-1.123e-4") == Token(
+            TokenKind.FLOAT, 0, 9, 1, 1, None, "-1.123e-4"
+        )
+        assert lex_one("-1.123e+4") == Token(
+            TokenKind.FLOAT, 0, 9, 1, 1, None, "-1.123e+4"
+        )
+        assert lex_one("-1.123e4567") == Token(
+            TokenKind.FLOAT, 0, 11, 1, 1, None, "-1.123e4567"
+        )
+
+    def lex_reports_useful_number_errors():
+        assert_syntax_error(
+            "00", "Invalid number, unexpected digit after 0: '0'.", (1, 2)
+        )
+        assert_syntax_error(
+            "01", "Invalid number, unexpected digit after 0: '1'.", (1, 2)
+        )
+        assert_syntax_error(
+            "01.23", "Invalid number, unexpected digit after 0: '1'.", (1, 2)
+        )
+        assert_syntax_error("+1", "Cannot parse the unexpected character '+'.", (1, 1))
+        assert_syntax_error(
+            "1.", "Invalid number, expected digit but got: <EOF>.", (1, 3)
+        )
+        assert_syntax_error(
+            "1e", "Invalid number, expected digit but got: <EOF>.", (1, 3)
+        )
+        assert_syntax_error(
+            "1E", "Invalid number, expected digit but got: <EOF>.", (1, 3)
+        )
+        assert_syntax_error(
+            "1.e1", "Invalid number, expected digit but got: 'e'.", (1, 3)
+        )
+        assert_syntax_error(
+            ".123", "Cannot parse the unexpected character '.'.", (1, 1)
+        )
+        assert_syntax_error(
+            "1.A", "Invalid number, expected digit but got: 'A'.", (1, 3)
+        )
+        assert_syntax_error(
+            "-A", "Invalid number, expected digit but got: 'A'.", (1, 2)
+        )
+        assert_syntax_error(
+            "1.0e", "Invalid number, expected digit but got: <EOF>.", (1, 5)
+        )
+        assert_syntax_error(
+            "1.0eA", "Invalid number, expected digit but got: 'A'.", (1, 5)
+        )
+        assert_syntax_error(
+            "1.2e3e", "Invalid number, expected digit but got: 'e'.", (1, 6)
+        )
+        assert_syntax_error(
+            "1.2e3.4", "Invalid number, expected digit but got: '.'.", (1, 6)
+        )
+        assert_syntax_error(
+            "1.23.4", "Invalid number, expected digit but got: '.'.", (1, 5)
+        )
+
+    def lex_does_not_allow_name_start_after_a_number():
+        assert_syntax_error(
+            "0xF1", "Invalid number, expected digit but got: 'x'.", (1, 2)
+        )
+        assert_syntax_error(
+            "0b10", "Invalid number, expected digit but got: 'b'.", (1, 2)
+        )
+        assert_syntax_error(
+            "123abc", "Invalid number, expected digit but got: 'a'.", (1, 4)
+        )
+        assert_syntax_error(
+            "1_1234", "Invalid number, expected digit but got: '_'.", (1, 2)
+        )
+        assert_syntax_error(
+            "1_1234", "Invalid number, expected digit but got: '_'.", (1, 2)
+        )
+        assert_syntax_error(
+            "1ß", "Cannot parse the unexpected character 'ß'.", (1, 2),
+        )
+        assert_syntax_error(
+            "1.23f", "Invalid number, expected digit but got: 'f'.", (1, 5)
+        )
+        assert_syntax_error(
+            "12ß", "Cannot parse the unexpected character 'ß'.", (1, 3),
+        )
+
+    # noinspection PyArgumentEqualDefault
+    def lexes_punctuation():
+        assert lex_one("!") == Token(TokenKind.BANG, 0, 1, 1, 1, None, None)
+        assert lex_one("$") == Token(TokenKind.DOLLAR, 0, 1, 1, 1, None, None)
+        assert lex_one("(") == Token(TokenKind.PAREN_L, 0, 1, 1, 1, None, None)
+        assert lex_one(")") == Token(TokenKind.PAREN_R, 0, 1, 1, 1, None, None)
+        assert lex_one("...") == Token(TokenKind.SPREAD, 0, 3, 1, 1, None, None)
+        assert lex_one(":") == Token(TokenKind.COLON, 0, 1, 1, 1, None, None)
+        assert lex_one("=") == Token(TokenKind.EQUALS, 0, 1, 1, 1, None, None)
+        assert lex_one("@") == Token(TokenKind.AT, 0, 1, 1, 1, None, None)
+        assert lex_one("[") == Token(TokenKind.BRACKET_L, 0, 1, 1, 1, None, None)
+        assert lex_one("]") == Token(TokenKind.BRACKET_R, 0, 1, 1, 1, None, None)
+        assert lex_one("{") == Token(TokenKind.BRACE_L, 0, 1, 1, 1, None, None)
+        assert lex_one("}") == Token(TokenKind.BRACE_R, 0, 1, 1, 1, None, None)
+        assert lex_one("|") == Token(TokenKind.PIPE, 0, 1, 1, 1, None, None)
+
+    def lex_reports_useful_unknown_character_error():
+        assert_syntax_error("..", "Cannot parse the unexpected character '.'.", (1, 1))
+        assert_syntax_error("?", "Cannot parse the unexpected character '?'.", (1, 1))
+        assert_syntax_error(
+            "\u203B", "Cannot parse the unexpected character '\u203B'.", (1, 1)
+        )
+        assert_syntax_error(
+            "\u200b", "Cannot parse the unexpected character '\\u200b'.", (1, 1)
+        )
+
+    # noinspection PyArgumentEqualDefault
+    def lex_reports_useful_information_for_dashes_in_names():
+        source = Source("a-b")
+        lexer = Lexer(source)
+        first_token = lexer.advance()
+        assert first_token == Token(TokenKind.NAME, 0, 1, 1, 1, None, "a")
+        with raises(GraphQLSyntaxError) as exc_info:
+            lexer.advance()
+        error = exc_info.value
+        assert error.message == (
+            "Syntax Error: Invalid number, expected digit but got: 'b'."
+        )
+        assert error.locations == [(1, 3)]
+
+    def produces_double_linked_list_of_tokens_including_comments():
+        source = Source(
+            """
+            {
+              #comment
+              field
+            }
+            """
+        )
+        lexer = Lexer(source)
+        start_token = lexer.token
+        while True:
+            end_token = lexer.advance()
+            if end_token.kind == TokenKind.EOF:
+                break
+            assert end_token.kind != TokenKind.COMMENT
+        assert start_token.prev is None
+        assert end_token.next is None
+        tokens: List[Token] = []
+        tok: Optional[Token] = start_token
+        while tok:
+            assert not tokens or tok.prev == tokens[-1]
+            tokens.append(tok)
+            tok = tok.next
+        assert [tok.kind for tok in tokens] == [
+            TokenKind.SOF,
+            TokenKind.BRACE_L,
+            TokenKind.COMMENT,
+            TokenKind.NAME,
+            TokenKind.BRACE_R,
+            TokenKind.EOF,
+        ]
+
+
+def describe_is_punctuator_token_kind():
+    def _is_punctuator_token(text: str) -> bool:
+        return is_punctuator_token_kind(lex_one(text).kind)
+
+    def returns_true_for_punctuator_tokens():
+        assert _is_punctuator_token("!") is True
+        assert _is_punctuator_token("$") is True
+        assert _is_punctuator_token("&") is True
+        assert _is_punctuator_token("(") is True
+        assert _is_punctuator_token(")") is True
+        assert _is_punctuator_token("...") is True
+        assert _is_punctuator_token(":") is True
+        assert _is_punctuator_token("=") is True
+        assert _is_punctuator_token("@") is True
+        assert _is_punctuator_token("[") is True
+        assert _is_punctuator_token("]") is True
+        assert _is_punctuator_token("{") is True
+        assert _is_punctuator_token("|") is True
+        assert _is_punctuator_token("}") is True
+
+    def returns_false_for_non_punctuator_tokens():
+        assert _is_punctuator_token("") is False
+        assert _is_punctuator_token("name") is False
+        assert _is_punctuator_token("1") is False
+        assert _is_punctuator_token("3.14") is False
+        assert _is_punctuator_token('"str"') is False
+        assert _is_punctuator_token('"""str"""') is False
diff --git a/tests/language/test_location.py b/tests/language/test_location.py
new file mode 100644
index 0000000..62096c1
--- /dev/null
+++ b/tests/language/test_location.py
@@ -0,0 +1,43 @@
+from graphql import SourceLocation
+
+
+def describe_source_location():
+    def can_be_formatted():
+        location = SourceLocation(1, 2)
+        assert location.formatted == {"line": 1, "column": 2}
+
+    def can_compare_with_other_source_location():
+        location = SourceLocation(1, 2)
+        same_location = SourceLocation(1, 2)
+        assert location == same_location
+        assert not location != same_location
+        different_location = SourceLocation(1, 1)
+        assert not location == different_location
+        assert location != different_location
+        different_location = SourceLocation(2, 2)
+        assert not location == different_location
+        assert location != different_location
+
+    def can_compare_with_location_tuple():
+        location = SourceLocation(1, 2)
+        same_location = (1, 2)
+        assert location == same_location
+        assert not location != same_location
+        different_location = (1, 1)
+        assert not location == different_location
+        assert location != different_location
+        different_location = (2, 2)
+        assert not location == different_location
+        assert location != different_location
+
+    def can_compare_with_formatted_location():
+        location = SourceLocation(1, 2)
+        same_location = location.formatted
+        assert location == same_location
+        assert not location != same_location
+        different_location = SourceLocation(1, 1).formatted
+        assert not location == different_location
+        assert location != different_location
+        different_location = SourceLocation(2, 2).formatted
+        assert not location == different_location
+        assert location != different_location
diff --git a/tests/language/test_parser.py b/tests/language/test_parser.py
new file mode 100644
index 0000000..573ef7c
--- /dev/null
+++ b/tests/language/test_parser.py
@@ -0,0 +1,519 @@
+from typing import cast
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLSyntaxError
+from graphql.language import (
+    ArgumentNode,
+    DefinitionNode,
+    DocumentNode,
+    FieldNode,
+    IntValueNode,
+    ListTypeNode,
+    ListValueNode,
+    NameNode,
+    NamedTypeNode,
+    NonNullTypeNode,
+    NullValueNode,
+    OperationDefinitionNode,
+    OperationType,
+    SelectionSetNode,
+    StringValueNode,
+    ValueNode,
+    Token,
+    TokenKind,
+    parse,
+    parse_type,
+    parse_value,
+    Source,
+)
+from graphql.pyutils import inspect
+
+from ..fixtures import kitchen_sink_query  # noqa: F401
+from ..utils import dedent
+
+
+def assert_syntax_error(text, message, location):
+    with raises(GraphQLSyntaxError) as exc_info:
+        parse(text)
+    error = exc_info.value
+    assert error.message == f"Syntax Error: {message}"
+    assert error.description == message
+    assert error.locations == [location]
+
+
+def describe_parser():
+    def asserts_that_a_source_to_parse_was_provided():
+        with raises(TypeError) as exc_info:
+            # noinspection PyArgumentList
+            assert parse()  # type: ignore
+        msg = str(exc_info.value)
+        assert "missing" in msg
+        assert "source" in msg
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            assert parse(None)  # type: ignore
+        msg = str(exc_info.value)
+        assert "Must provide Source. Received: None." in msg
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            assert parse({})  # type: ignore
+        msg = str(exc_info.value)
+        assert "Must provide Source. Received: {}." in msg
+
+    def parse_provides_useful_errors():
+        with raises(GraphQLSyntaxError) as exc_info:
+            parse("{")
+        error = exc_info.value
+        assert error.message == "Syntax Error: Expected Name, found <EOF>."
+        assert error.positions == [1]
+        assert error.locations == [(1, 2)]
+        assert str(error) + "\n" == dedent(
+            """
+            Syntax Error: Expected Name, found <EOF>.
+
+            GraphQL request:1:2
+            1 | {
+              |  ^
+            """
+        )
+        assert_syntax_error(
+            "\n      { ...MissingOn }\n      fragment MissingOn Type",
+            "Expected 'on', found Name 'Type'.",
+            (3, 26),
+        )
+        assert_syntax_error("{ field: {} }", "Expected Name, found '{'.", (1, 10))
+        assert_syntax_error(
+            "notAnOperation Foo { field }", "Unexpected Name 'notAnOperation'.", (1, 1)
+        )
+        assert_syntax_error("...", "Unexpected '...'.", (1, 1))
+        assert_syntax_error('{ ""', "Expected Name, found String ''.", (1, 3))
+
+    def parse_provides_useful_error_when_using_source():
+        with raises(GraphQLSyntaxError) as exc_info:
+            parse(Source("query", "MyQuery.graphql"))
+        error = exc_info.value
+        assert str(error) + "\n" == dedent(
+            """
+            Syntax Error: Expected '{', found <EOF>.
+
+            MyQuery.graphql:1:6
+            1 | query
+              |      ^
+            """
+        )
+
+    def parses_variable_inline_values():
+        parse("{ field(complex: { a: { b: [ $var ] } }) }")
+
+    def parses_constant_default_values():
+        assert_syntax_error(
+            "query Foo($x: Complex = { a: { b: [ $var ] } }) { field }",
+            "Unexpected '$'.",
+            (1, 37),
+        )
+
+    def parses_variable_definition_directives():
+        parse("query Foo($x: Boolean = false @bar) { field }")
+
+    def does_not_accept_fragments_named_on():
+        assert_syntax_error(
+            "fragment on on on { on }", "Unexpected Name 'on'.", (1, 10)
+        )
+
+    def does_not_accept_fragments_spread_of_on():
+        assert_syntax_error("{ ...on }", "Expected Name, found '}'.", (1, 9))
+
+    def parses_multi_byte_characters():
+        # Note: \u0A0A could be naively interpreted as two line-feed chars.
+        doc = parse(
+            """
+            # This comment has a \u0A0A multi-byte character.
+            { field(arg: "Has a \u0A0A multi-byte character.") }
+            """
+        )
+        definitions = doc.definitions
+        assert isinstance(definitions, list)
+        assert len(definitions) == 1
+        selection_set = cast(OperationDefinitionNode, definitions[0]).selection_set
+        selections = selection_set.selections
+        assert isinstance(selections, list)
+        assert len(selections) == 1
+        arguments = cast(FieldNode, selections[0]).arguments
+        assert isinstance(arguments, list)
+        assert len(arguments) == 1
+        value = arguments[0].value
+        assert isinstance(value, StringValueNode)
+        assert value.value == "Has a \u0A0A multi-byte character."
+
+    # noinspection PyShadowingNames
+    def parses_kitchen_sink(kitchen_sink_query):  # noqa: F811
+        parse(kitchen_sink_query)
+
+    def allows_non_keywords_anywhere_a_name_is_allowed():
+        non_keywords = (
+            "on",
+            "fragment",
+            "query",
+            "mutation",
+            "subscription",
+            "true",
+            "false",
+        )
+        for keyword in non_keywords:
+            # You can't define or reference a fragment named `on`.
+            fragment_name = "a" if keyword == "on" else keyword
+            document = f"""
+                query {keyword} {{
+                  ... {fragment_name}
+                  ... on {keyword} {{ field }}
+                }}
+                fragment {fragment_name} on Type {{
+                  {keyword}({keyword}: ${keyword})
+                    @{keyword}({keyword}: {keyword})
+                }}
+                """
+            parse(document)
+
+    def parses_anonymous_mutation_operations():
+        parse(
+            """
+            mutation {
+              mutationField
+            }
+            """
+        )
+
+    def parses_anonymous_subscription_operations():
+        parse(
+            """
+            subscription {
+              subscriptionField
+            }
+            """
+        )
+
+    def parses_named_mutation_operations():
+        parse(
+            """
+            mutation Foo {
+              mutationField
+            }
+            """
+        )
+
+    def parses_named_subscription_operations():
+        parse(
+            """
+            subscription Foo {
+              subscriptionField
+            }
+            """
+        )
+
+    def creates_ast():
+        doc = parse(
+            dedent(
+                """
+                {
+                  node(id: 4) {
+                    id,
+                    name
+                  }
+                }
+                """
+            )
+        )
+        assert isinstance(doc, DocumentNode)
+        assert doc.loc == (0, 41)
+        definitions = doc.definitions
+        assert isinstance(definitions, list)
+        assert len(definitions) == 1
+        definition = cast(OperationDefinitionNode, definitions[0])
+        assert isinstance(definition, DefinitionNode)
+        assert definition.loc == (0, 40)
+        assert definition.operation == OperationType.QUERY
+        assert definition.name is None
+        assert definition.variable_definitions == []
+        assert definition.directives == []
+        selection_set = definition.selection_set
+        assert isinstance(selection_set, SelectionSetNode)
+        assert selection_set.loc == (0, 40)
+        selections = selection_set.selections
+        assert isinstance(selections, list)
+        assert len(selections) == 1
+        field = selections[0]
+        assert isinstance(field, FieldNode)
+        assert field.loc == (4, 38)
+        assert field.alias is None
+        name = field.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (4, 8)
+        assert name.value == "node"
+        arguments = field.arguments
+        assert isinstance(arguments, list)
+        assert len(arguments) == 1
+        argument = arguments[0]
+        assert isinstance(argument, ArgumentNode)
+        name = argument.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (9, 11)
+        assert name.value == "id"
+        value = argument.value
+        assert isinstance(value, ValueNode)
+        assert isinstance(value, IntValueNode)
+        assert value.loc == (13, 14)
+        assert value.value == "4"
+        assert argument.loc == (9, 14)
+        assert field.directives == []
+        selection_set = field.selection_set  # type: ignore
+        assert isinstance(selection_set, SelectionSetNode)
+        selections = selection_set.selections
+        assert isinstance(selections, list)
+        assert len(selections) == 2
+        field = selections[0]
+        assert isinstance(field, FieldNode)
+        assert field.loc == (22, 24)
+        assert field.alias is None
+        name = field.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (22, 24)
+        assert name.value == "id"
+        assert field.arguments == []
+        assert field.directives == []
+        assert field.selection_set is None
+        field = selections[0]
+        assert isinstance(field, FieldNode)
+        assert field.loc == (22, 24)
+        assert field.alias is None
+        name = field.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (22, 24)
+        assert name.value == "id"
+        assert field.arguments == []
+        assert field.directives == []
+        assert field.selection_set is None
+        field = selections[1]
+        assert isinstance(field, FieldNode)
+        assert field.loc == (30, 34)
+        assert field.alias is None
+        name = field.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (30, 34)
+        assert name.value == "name"
+        assert field.arguments == []
+        assert field.directives == []
+        assert field.selection_set is None
+
+    def creates_ast_from_nameless_query_without_variables():
+        doc = parse(
+            dedent(
+                """
+                query {
+                  node {
+                    id
+                  }
+                }
+                """
+            )
+        )
+        assert isinstance(doc, DocumentNode)
+        assert doc.loc == (0, 30)
+        definitions = doc.definitions
+        assert isinstance(definitions, list)
+        assert len(definitions) == 1
+        definition = definitions[0]
+        assert isinstance(definition, OperationDefinitionNode)
+        assert definition.loc == (0, 29)
+        assert definition.operation == OperationType.QUERY
+        assert definition.name is None
+        assert definition.variable_definitions == []
+        assert definition.directives == []
+        selection_set = definition.selection_set
+        assert isinstance(selection_set, SelectionSetNode)
+        assert selection_set.loc == (6, 29)
+        selections = selection_set.selections
+        assert isinstance(selections, list)
+        assert len(selections) == 1
+        field = selections[0]
+        assert isinstance(field, FieldNode)
+        assert field.loc == (10, 27)
+        assert field.alias is None
+        name = field.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (10, 14)
+        assert name.value == "node"
+        assert field.arguments == []
+        assert field.directives == []
+        selection_set = field.selection_set  # type: ignore
+        assert isinstance(selection_set, SelectionSetNode)
+        assert selection_set.loc == (15, 27)
+        selections = selection_set.selections
+        assert isinstance(selections, list)
+        assert len(selections) == 1
+        field = selections[0]
+        assert isinstance(field, FieldNode)
+        assert field.loc == (21, 23)
+        assert field.alias is None
+        name = field.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (21, 23)
+        assert name.value == "id"
+        assert field.arguments == []
+        assert field.directives == []
+        assert field.selection_set is None
+
+    def allows_parsing_without_source_location_information():
+        result = parse("{ id }", no_location=True)
+        assert result.loc is None
+
+    def experimental_allows_parsing_fragment_defined_variables():
+        document = "fragment a($v: Boolean = false) on t { f(v: $v) }"
+        parse(document, experimental_fragment_variables=True)
+        with raises(GraphQLSyntaxError):
+            parse(document)
+
+    def contains_location_information_that_only_stringifies_start_end():
+        result = parse("{ id }")
+        assert str(result.loc) == "0:6"
+        assert repr(result.loc) == "<Location 0:6>"
+        assert inspect(result.loc) == repr(result.loc)
+
+    def contains_references_to_source():
+        source = Source("{ id }")
+        result = parse(source)
+        assert result.loc and result.loc.source is source
+
+    def contains_references_to_start_and_end_tokens():
+        result = parse("{ id }")
+        start_token = result.loc and result.loc.start_token
+        assert isinstance(start_token, Token)
+        assert start_token.kind == TokenKind.SOF
+        end_token = result.loc and result.loc.end_token
+        assert isinstance(end_token, Token)
+        assert end_token.kind == TokenKind.EOF
+
+    def allows_comments_everywhere_in_the_source():
+        # make sure first and last line can be comment
+        result = parse(
+            """# top comment
+            {
+              field # field comment
+            }
+            # bottom comment"""
+        )
+        top_comment = result.loc and result.loc.start_token.next
+        assert top_comment and top_comment.kind is TokenKind.COMMENT
+        assert top_comment.value == " top comment"
+        field_comment = top_comment.next.next.next  # type: ignore
+        assert field_comment and field_comment.kind is TokenKind.COMMENT
+        assert field_comment.value == " field comment"
+        bottom_comment = field_comment.next.next  # type: ignore
+        assert bottom_comment and bottom_comment.kind is TokenKind.COMMENT
+        assert bottom_comment.value == " bottom comment"
+
+
+def describe_parse_value():
+    def parses_null_value():
+        result = parse_value("null")
+        assert isinstance(result, NullValueNode)
+        assert result.loc == (0, 4)
+
+    def parses_empty_strings():
+        result = parse_value('""')
+        assert isinstance(result, StringValueNode)
+        assert result.value == ""
+        assert result.loc == (0, 2)
+
+    def parses_list_values():
+        result = parse_value('[123 "abc"]')
+        assert isinstance(result, ListValueNode)
+        assert result.loc == (0, 11)
+        values = result.values
+        assert isinstance(values, list)
+        assert len(values) == 2
+        value = values[0]
+        assert isinstance(value, IntValueNode)
+        assert value.loc == (1, 4)
+        assert value.value == "123"
+        value = values[1]
+        assert isinstance(value, StringValueNode)
+        assert value.loc == (5, 10)
+        assert value.value == "abc"
+
+    def parses_block_strings():
+        result = parse_value('["""long""" "short"]')
+        assert isinstance(result, ListValueNode)
+        assert result.loc == (0, 20)
+        values = result.values
+        assert isinstance(values, list)
+        assert len(values) == 2
+        value = values[0]
+        assert isinstance(value, StringValueNode)
+        assert value.loc == (1, 11)
+        assert value.value == "long"
+        assert value.block is True
+        value = values[1]
+        assert isinstance(value, StringValueNode)
+        assert value.loc == (12, 19)
+        assert value.value == "short"
+        assert value.block is False
+
+
+def describe_parse_type():
+    def parses_well_known_types():
+        result = parse_type("String")
+        assert isinstance(result, NamedTypeNode)
+        assert result.loc == (0, 6)
+        name = result.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (0, 6)
+        assert name.value == "String"
+
+    def parses_custom_types():
+        result = parse_type("MyType")
+        assert isinstance(result, NamedTypeNode)
+        assert result.loc == (0, 6)
+        name = result.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (0, 6)
+        assert name.value == "MyType"
+
+    def parses_list_types():
+        result = parse_type("[MyType]")
+        assert isinstance(result, ListTypeNode)
+        assert result.loc == (0, 8)
+        type_ = result.type
+        assert isinstance(type_, NamedTypeNode)
+        assert type_.loc == (1, 7)
+        name = type_.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (1, 7)
+        assert name.value == "MyType"
+
+    def parses_non_null_types():
+        result = parse_type("MyType!")
+        assert isinstance(result, NonNullTypeNode)
+        assert result.loc == (0, 7)
+        type_ = result.type
+        assert isinstance(type_, NamedTypeNode)
+        assert type_.loc == (0, 6)
+        name = type_.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (0, 6)
+        assert name.value == "MyType"
+
+    def parses_nested_types():
+        result = parse_type("[MyType!]")
+        assert isinstance(result, ListTypeNode)
+        assert result.loc == (0, 9)
+        type_ = result.type
+        assert isinstance(type_, NonNullTypeNode)
+        assert type_.loc == (1, 8)
+        type_ = type_.type
+        assert isinstance(type_, NamedTypeNode)
+        assert type_.loc == (1, 7)
+        name = type_.name
+        assert isinstance(name, NameNode)
+        assert name.loc == (1, 7)
+        assert name.value == "MyType"
diff --git a/tests/language/test_predicates.py b/tests/language/test_predicates.py
new file mode 100644
index 0000000..08c95aa
--- /dev/null
+++ b/tests/language/test_predicates.py
@@ -0,0 +1,153 @@
+from operator import attrgetter
+from typing import Callable
+
+from graphql.language import (
+    ast,
+    Node,
+    is_definition_node,
+    is_executable_definition_node,
+    is_selection_node,
+    is_value_node,
+    is_type_node,
+    is_type_system_definition_node,
+    is_type_definition_node,
+    is_type_system_extension_node,
+    is_type_extension_node,
+)
+
+all_ast_nodes = sorted(
+    [
+        node_type()
+        for node_type in vars(ast).values()
+        if type(node_type) is type and issubclass(node_type, Node)
+    ],
+    key=attrgetter("kind"),
+)
+
+
+def filter_nodes(predicate: Callable[[Node], bool]):
+    return [node.kind for node in all_ast_nodes if predicate(node)]
+
+
+def describe_ast_node_predicates():
+    def check_definition_node():
+        assert filter_nodes(is_definition_node) == [
+            "definition",
+            "directive_definition",
+            "enum_type_definition",
+            "enum_type_extension",
+            "enum_value_definition",
+            "executable_definition",
+            "field_definition",
+            "fragment_definition",
+            "input_object_type_definition",
+            "input_object_type_extension",
+            "input_value_definition",
+            "interface_type_definition",
+            "interface_type_extension",
+            "object_type_definition",
+            "object_type_extension",
+            "operation_definition",
+            "scalar_type_definition",
+            "scalar_type_extension",
+            "schema_definition",
+            "type_definition",
+            "type_extension",
+            "type_system_definition",
+            "union_type_definition",
+            "union_type_extension",
+        ]
+
+    def check_executable_definition_node():
+        assert filter_nodes(is_executable_definition_node) == [
+            "executable_definition",
+            "fragment_definition",
+            "operation_definition",
+        ]
+
+    def check_selection_node():
+        assert filter_nodes(is_selection_node) == [
+            "field",
+            "fragment_spread",
+            "inline_fragment",
+            "selection",
+        ]
+
+    def check_value_node():
+        assert filter_nodes(is_value_node) == [
+            "boolean_value",
+            "enum_value",
+            "float_value",
+            "int_value",
+            "list_value",
+            "null_value",
+            "object_value",
+            "string_value",
+            "value",
+            "variable",
+        ]
+
+    def check_type_node():
+        assert filter_nodes(is_type_node) == [
+            "list_type",
+            "named_type",
+            "non_null_type",
+            "type",
+        ]
+
+    def check_type_system_definition_node():
+        assert filter_nodes(is_type_system_definition_node) == [
+            "directive_definition",
+            "enum_type_definition",
+            "enum_type_extension",
+            "enum_value_definition",
+            "input_object_type_definition",
+            "input_object_type_extension",
+            "interface_type_definition",
+            "interface_type_extension",
+            "object_type_definition",
+            "object_type_extension",
+            "scalar_type_definition",
+            "scalar_type_extension",
+            "schema_definition",
+            "type_definition",
+            "type_extension",
+            "type_system_definition",
+            "union_type_definition",
+            "union_type_extension",
+        ]
+
+    def check_type_definition_node():
+        assert filter_nodes(is_type_definition_node) == [
+            "enum_type_definition",
+            "enum_value_definition",
+            "input_object_type_definition",
+            "interface_type_definition",
+            "object_type_definition",
+            "scalar_type_definition",
+            "type_definition",
+            "union_type_definition",
+        ]
+
+    def check_type_system_extension_node():
+        assert filter_nodes(is_type_system_extension_node) == [
+            "enum_type_extension",
+            "input_object_type_extension",
+            "interface_type_extension",
+            "object_type_extension",
+            "scalar_type_extension",
+            "schema_extension",
+            "type_extension",
+            "union_type_extension",
+        ]
+
+    def check_type_extension_node():
+        assert filter_nodes(is_type_extension_node) == [
+            "enum_type_extension",
+            "input_object_type_extension",
+            "interface_type_extension",
+            "object_type_extension",
+            "scalar_type_extension",
+            "type_extension",
+            "union_type_extension",
+        ]
diff --git a/tests/language/test_printer.py b/tests/language/test_printer.py
new file mode 100644
index 0000000..6ba678c
--- /dev/null
+++ b/tests/language/test_printer.py
@@ -0,0 +1,166 @@
+from copy import deepcopy
+
+from pytest import raises  # type: ignore
+
+from graphql.language import FieldNode, NameNode, parse, print_ast
+
+from ..fixtures import kitchen_sink_query  # noqa: F401
+from ..utils import dedent
+
+
+def describe_printer_query_document():
+
+    # noinspection PyShadowingNames
+    def does_not_alter_ast(kitchen_sink_query):  # noqa: F811
+        ast = parse(kitchen_sink_query)
+        ast_before = deepcopy(ast)
+        print_ast(ast)
+        assert ast == ast_before
+
+    def prints_minimal_ast():
+        ast = FieldNode(name=NameNode(value="foo"))
+        assert print_ast(ast) == "foo"
+
+    def produces_helpful_error_messages():
+        bad_ast = {"random": "Data"}
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            print_ast(bad_ast)  # type: ignore
+        assert str(exc_info.value) == "Not an AST Node: {'random': 'Data'}."
+        corrupt_ast = FieldNode(name="random data")
+        with raises(TypeError) as exc_info:
+            print_ast(corrupt_ast)
+        assert str(exc_info.value) == "Invalid AST Node: 'random data'."
+
+    def correctly_prints_query_operation_without_name():
+        query_ast_shorthanded = parse("query { id, name }")
+        assert print_ast(query_ast_shorthanded) == "{\n  id\n  name\n}\n"
+
+    def correctly_prints_mutation_operation_without_name():
+        mutation_ast = parse("mutation { id, name }")
+        assert print_ast(mutation_ast) == "mutation {\n  id\n  name\n}\n"
+
+    def correctly_prints_query_operation_with_artifacts():
+        query_ast_with_artifacts = parse(
+            "query ($foo: TestType) @testDirective { id, name }"
+        )
+        assert print_ast(query_ast_with_artifacts) == dedent(
+            """
+            query ($foo: TestType) @testDirective {
+              id
+              name
+            }
+            """
+        )
+
+    def correctly_prints_mutation_operation_with_artifacts():
+        mutation_ast_with_artifacts = parse(
+            "mutation ($foo: TestType) @testDirective { id, name }"
+        )
+        assert print_ast(mutation_ast_with_artifacts) == dedent(
+            """
+            mutation ($foo: TestType) @testDirective {
+              id
+              name
+            }
+            """
+        )
+
+    def prints_query_with_variable_directives():
+        query_ast_with_variable_directive = parse(
+            "query ($foo: TestType = {a: 123}" " @testDirective(if: true) @test) { id }"
+        )
+        assert print_ast(query_ast_with_variable_directive) == dedent(
+            """
+            query ($foo: TestType = {a: 123} @testDirective(if: true) @test) {
+              id
+            }
+            """
+        )
+
+    def experimental_prints_fragment_with_variable_directives():
+        query_ast_with_variable_directive = parse(
+            "fragment Foo($foo: TestType @test) on TestType @testDirective { id }",
+            experimental_fragment_variables=True,
+        )
+        assert print_ast(query_ast_with_variable_directive) == dedent(
+            """
+            fragment Foo($foo: TestType @test) on TestType @testDirective {
+              id
+            }
+            """
+        )
+
+    def experimental_correctly_prints_fragment_defined_variables():
+        source = """
+            fragment Foo($a: ComplexType, $b: Boolean = false) on TestType {
+              id
+            }
+            """
+        fragment_with_variable = parse(source, experimental_fragment_variables=True)
+        assert print_ast(fragment_with_variable) == dedent(source)
+
+    # noinspection PyShadowingNames
+    def prints_kitchen_sink(kitchen_sink_query):  # noqa: F811
+        ast = parse(kitchen_sink_query)
+        printed = print_ast(ast)
+        assert printed == dedent(
+            r'''
+            query queryName($foo: ComplexType, $site: Site = MOBILE) @onQuery {
+              whoever123is: node(id: [123, 456]) {
+                id
+                ... on User @onInlineFragment {
+                  field2 {
+                    id
+                    alias: field1(first: 10, after: $foo) @include(if: $foo) {
+                      id
+                      ...frag @onFragmentSpread
+                    }
+                  }
+                }
+                ... @skip(unless: $foo) {
+                  id
+                }
+                ... {
+                  id
+                }
+              }
+            }
+
+            mutation likeStory @onMutation {
+              like(story: 123) @onField {
+                story {
+                  id @onField
+                }
+              }
+            }
+
+            subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) @onSubscription {
+              storyLikeSubscribe(input: $input) {
+                story {
+                  likers {
+                    count
+                  }
+                  likeSentence {
+                    text
+                  }
+                }
+              }
+            }
+
+            fragment frag on Friend @onFragmentDefinition {
+              foo(size: $size, bar: $b, obj: {key: "value", block: """
+                block string uses \"""
+              """})
+            }
+
+            {
+              unnamed(truthy: true, falsy: false, nullish: null)
+              query
+            }
+
+            {
+              __typename
+            }
+            '''  # noqa: E501
+        )
diff --git a/tests/language/test_schema_parser.py b/tests/language/test_schema_parser.py
new file mode 100644
index 0000000..cacfa65
--- /dev/null
+++ b/tests/language/test_schema_parser.py
@@ -0,0 +1,792 @@
+from textwrap import dedent
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLSyntaxError
+from graphql.language import (
+    BooleanValueNode,
+    DirectiveDefinitionNode,
+    DirectiveNode,
+    DocumentNode,
+    EnumTypeDefinitionNode,
+    EnumValueDefinitionNode,
+    FieldDefinitionNode,
+    InputObjectTypeDefinitionNode,
+    InputValueDefinitionNode,
+    InterfaceTypeDefinitionNode,
+    InterfaceTypeExtensionNode,
+    ListTypeNode,
+    NameNode,
+    NamedTypeNode,
+    NonNullTypeNode,
+    ObjectTypeDefinitionNode,
+    ObjectTypeExtensionNode,
+    OperationType,
+    OperationTypeDefinitionNode,
+    ScalarTypeDefinitionNode,
+    SchemaDefinitionNode,
+    SchemaExtensionNode,
+    StringValueNode,
+    UnionTypeDefinitionNode,
+    parse,
+)
+
+from ..fixtures import kitchen_sink_sdl  # noqa: F401
+
+
+def assert_syntax_error(text, message, location):
+    with raises(GraphQLSyntaxError) as exc_info:
+        parse(text)
+    error = exc_info.value
+    assert error.message == f"Syntax Error: {message}"
+    assert error.description == message
+    assert error.locations == [location]
+
+
+def assert_definitions(body, loc, num=1):
+    doc = parse(body)
+    assert isinstance(doc, DocumentNode)
+    assert doc.loc == loc
+    definitions = doc.definitions
+    assert isinstance(definitions, list)
+    assert len(definitions) == num
+    return definitions[0] if num == 1 else definitions
+
+
+def type_node(name, loc):
+    return NamedTypeNode(name=name_node(name, loc), loc=loc)
+
+
+def name_node(name, loc):
+    return NameNode(value=name, loc=loc)
+
+
+def field_node(name, type_, loc):
+    return field_node_with_args(name, type_, [], loc)
+
+
+def field_node_with_args(name, type_, args, loc):
+    return FieldDefinitionNode(
+        name=name, arguments=args, type=type_, directives=[], loc=loc, description=None
+    )
+
+
+def non_null_type(type_, loc):
+    return NonNullTypeNode(type=type_, loc=loc)
+
+
+def enum_value_node(name, loc):
+    return EnumValueDefinitionNode(
+        name=name_node(name, loc), directives=[], loc=loc, description=None
+    )
+
+
+def input_value_node(name, type_, default_value, loc):
+    return InputValueDefinitionNode(
+        name=name,
+        type=type_,
+        default_value=default_value,
+        directives=[],
+        loc=loc,
+        description=None,
+    )
+
+
+def boolean_value_node(value, loc):
+    return BooleanValueNode(value=value, loc=loc)
+
+
+def string_value_node(value, block, loc):
+    return StringValueNode(value=value, block=block, loc=loc)
+
+
+def list_type_node(type_, loc):
+    return ListTypeNode(type=type_, loc=loc)
+
+
+def schema_extension_node(directives, operation_types, loc):
+    return SchemaExtensionNode(
+        directives=directives, operation_types=operation_types, loc=loc
+    )
+
+
+def operation_type_definition(operation, type_, loc):
+    return OperationTypeDefinitionNode(operation=operation, type=type_, loc=loc)
+
+
+def directive_node(name, arguments, loc):
+    return DirectiveNode(name=name, arguments=arguments, loc=loc)
+
+
+def describe_schema_parser():
+    def simple_type():
+        body = dedent(
+            """
+            type Hello {
+              world: String
+            }
+            """
+        )
+        definition = assert_definitions(body, (0, 32))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.description is None
+        assert definition.interfaces == []
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("world", (16, 21)), type_node("String", (23, 29)), (16, 29)
+            )
+        ]
+        assert definition.loc == (1, 31)
+
+    def parses_type_with_description_string():
+        body = dedent(
+            """
+            "Description"
+            type Hello {
+              world: String
+            }
+            """
+        )
+        definition = assert_definitions(body, (0, 46))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (20, 25))
+        description = definition.description
+        assert description == string_value_node("Description", False, (1, 14))
+
+    def parses_type_with_description_multi_line_string():
+        body = dedent(
+            '''
+            """
+            Description
+            """
+            # Even with comments between them
+            type Hello {
+              world: String
+            }'''
+        )
+        definition = assert_definitions(body, (0, 85))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (60, 65))
+        description = definition.description
+        assert description == string_value_node("Description", True, (1, 20))
+
+    def parses_schema_with_description_string():
+        body = dedent(
+            """
+            "Description"
+            schema {
+              query: Foo
+            }
+            """
+        )
+        definition = assert_definitions(body, (0, 39))
+        assert isinstance(definition, SchemaDefinitionNode)
+        description = definition.description
+        assert description == string_value_node("Description", False, (1, 14))
+
+    def description_followed_by_something_other_than_type_system_definition_throws():
+        assert_syntax_error('"Description" 1', "Unexpected Int '1'.", (1, 15))
+
+    def simple_extension():
+        body = dedent(
+            """
+            extend type Hello {
+              world: String
+            }
+            """
+        )
+        extension = assert_definitions(body, (0, 39))
+        assert isinstance(extension, ObjectTypeExtensionNode)
+        assert extension.name == name_node("Hello", (13, 18))
+        assert extension.interfaces == []
+        assert extension.directives == []
+        assert extension.fields == [
+            field_node(
+                name_node("world", (23, 28)), type_node("String", (30, 36)), (23, 36)
+            )
+        ]
+        assert extension.loc == (1, 38)
+
+    def object_extension_without_fields():
+        body = "extend type Hello implements Greeting"
+        extension = assert_definitions(body, (0, 37))
+        assert isinstance(extension, ObjectTypeExtensionNode)
+        assert extension.name == name_node("Hello", (12, 17))
+        assert extension.interfaces == [type_node("Greeting", (29, 37))]
+        assert extension.directives == []
+        assert extension.fields == []
+        assert extension.loc == (0, 37)
+
+    def interface_extension_without_fields():
+        body = "extend interface Hello implements Greeting"
+        extension = assert_definitions(body, (0, 42))
+        assert isinstance(extension, InterfaceTypeExtensionNode)
+        assert extension.name == name_node("Hello", (17, 22))
+        assert extension.interfaces == [type_node("Greeting", (34, 42))]
+        assert extension.directives == []
+        assert extension.fields == []
+        assert extension.loc == (0, 42)
+
+    def object_extension_without_fields_followed_by_extension():
+        body = (
+            "\n      extend type Hello implements Greeting\n\n"
+            "      extend type Hello implements SecondGreeting\n    "
+        )
+        extensions = assert_definitions(body, (0, 100), 2)
+        extension = extensions[0]
+        assert isinstance(extension, ObjectTypeExtensionNode)
+        assert extension.name == name_node("Hello", (19, 24))
+        assert extension.interfaces == [type_node("Greeting", (36, 44))]
+        assert extension.directives == []
+        assert extension.fields == []
+        assert extension.loc == (7, 44)
+        extension = extensions[1]
+        assert isinstance(extension, ObjectTypeExtensionNode)
+        assert extension.name == name_node("Hello", (64, 69))
+        assert extension.interfaces == [type_node("SecondGreeting", (81, 95))]
+        assert extension.directives == []
+        assert extension.fields == []
+        assert extension.loc == (52, 95)
+
+    def extension_without_anything_throws():
+        assert_syntax_error("extend scalar Hello", "Unexpected <EOF>.", (1, 20))
+        assert_syntax_error("extend type Hello", "Unexpected <EOF>.", (1, 18))
+        assert_syntax_error("extend interface Hello", "Unexpected <EOF>.", (1, 23))
+        assert_syntax_error("extend union Hello", "Unexpected <EOF>.", (1, 19))
+        assert_syntax_error("extend enum Hello", "Unexpected <EOF>.", (1, 18))
+        assert_syntax_error("extend input Hello", "Unexpected <EOF>.", (1, 19))
+
+    def interface_extension_without_fields_followed_by_extension():
+        body = (
+            "\n      extend interface Hello implements Greeting\n\n"
+            "      extend interface Hello implements SecondGreeting\n    "
+        )
+        extensions = assert_definitions(body, (0, 110), 2)
+        extension = extensions[0]
+        assert isinstance(extension, InterfaceTypeExtensionNode)
+        assert extension.name == name_node("Hello", (24, 29))
+        assert extension.interfaces == [type_node("Greeting", (41, 49))]
+        assert extension.directives == []
+        assert extension.fields == []
+        assert extension.loc == (7, 49)
+        extension = extensions[1]
+        assert isinstance(extension, InterfaceTypeExtensionNode)
+        assert extension.name == name_node("Hello", (74, 79))
+        assert extension.interfaces == [type_node("SecondGreeting", (91, 105))]
+        assert extension.directives == []
+        assert extension.fields == []
+        assert extension.loc == (57, 105)
+
+    def object_extension_do_not_include_descriptions():
+        assert_syntax_error(
+            """
+            "Description"
+            extend type Hello {
+              world: String
+            }""",
+            "Unexpected Name 'extend'.",
+            (3, 13),
+        )
+        assert_syntax_error(
+            """
+            extend "Description" type Hello {
+              world: String
+            }""",
+            "Unexpected String 'Description'.",
+            (2, 20),
+        )
+
+    def interface_extension_do_not_include_descriptions():
+        assert_syntax_error(
+            """
+            "Description"
+            extend interface Hello {
+              world: String
+            }""",
+            "Unexpected Name 'extend'.",
+            (3, 13),
+        )
+        assert_syntax_error(
+            """
+            extend "Description" interface Hello {
+              world: String
+            }""",
+            "Unexpected String 'Description'.",
+            (2, 20),
+        )
+
+    def schema_extension():
+        body = """
+            extend schema {
+              mutation: Mutation
+            }"""
+        doc = parse(body)
+        assert isinstance(doc, DocumentNode)
+        assert doc.loc == (0, 75)
+        assert doc.definitions == [
+            schema_extension_node(
+                [],
+                [
+                    operation_type_definition(
+                        OperationType.MUTATION,
+                        type_node("Mutation", (53, 61)),
+                        (43, 61),
+                    )
+                ],
+                (13, 75),
+            )
+        ]
+
+    def schema_extension_with_only_directives():
+        body = "extend schema @directive"
+        doc = parse(body)
+        assert isinstance(doc, DocumentNode)
+        assert doc.loc == (0, 24)
+        assert doc.definitions == [
+            schema_extension_node(
+                [directive_node(name_node("directive", (15, 24)), [], (14, 24))],
+                [],
+                (0, 24),
+            )
+        ]
+
+    def schema_extension_without_anything_throws():
+        assert_syntax_error("extend schema", "Unexpected <EOF>.", (1, 14))
+
+    def schema_extension_with_invalid_operation_type_throws():
+        assert_syntax_error(
+            "extend schema { unknown: SomeType }", "Unexpected Name 'unknown'.", (1, 17)
+        )
+
+    def simple_non_null_type():
+        body = dedent(
+            """
+            type Hello {
+              world: String!
+            }
+            """
+        )
+        definition = assert_definitions(body, (0, 33))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.description is None
+        assert definition.interfaces == []
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("world", (16, 21)),
+                non_null_type(type_node("String", (23, 29)), (23, 30)),
+                (16, 30),
+            )
+        ]
+        assert definition.loc == (1, 32)
+
+    def simple_interface_inheriting_interface():
+        body = "interface Hello implements World { field: String }"
+        definition = assert_definitions(body, (0, 50))
+        assert isinstance(definition, InterfaceTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (10, 15))
+        assert definition.description is None
+        assert definition.interfaces == [type_node("World", (27, 32))]
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("field", (35, 40)), type_node("String", (42, 48)), (35, 48)
+            )
+        ]
+        assert definition.loc == (0, 50)
+
+    def simple_type_inheriting_interface():
+        body = "type Hello implements World { field: String }"
+        definition = assert_definitions(body, (0, 45))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (5, 10))
+        assert definition.description is None
+        assert definition.interfaces == [type_node("World", (22, 27))]
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("field", (30, 35)), type_node("String", (37, 43)), (30, 43)
+            )
+        ]
+        assert definition.loc == (0, 45)
+
+    def simple_type_inheriting_multiple_interfaces():
+        body = "type Hello implements Wo & rld { field: String }"
+        definition = assert_definitions(body, (0, 48))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (5, 10))
+        assert definition.description is None
+        assert definition.interfaces == [
+            type_node("Wo", (22, 24)),
+            type_node("rld", (27, 30)),
+        ]
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("field", (33, 38)), type_node("String", (40, 46)), (33, 46)
+            )
+        ]
+        assert definition.loc == (0, 48)
+
+    def simple_interface_inheriting_multiple_interfaces():
+        body = "interface Hello implements Wo & rld { field: String }"
+        definition = assert_definitions(body, (0, 53))
+        assert isinstance(definition, InterfaceTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (10, 15))
+        assert definition.description is None
+        assert definition.interfaces == [
+            type_node("Wo", (27, 29)),
+            type_node("rld", (32, 35)),
+        ]
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("field", (38, 43)), type_node("String", (45, 51)), (38, 51)
+            )
+        ]
+        assert definition.loc == (0, 53)
+
+    def simple_type_inheriting_multiple_interfaces_with_leading_ampersand():
+        body = "type Hello implements & Wo & rld { field: String }"
+        definition = assert_definitions(body, (0, 50))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (5, 10))
+        assert definition.description is None
+        assert definition.interfaces == [
+            type_node("Wo", (24, 26)),
+            type_node("rld", (29, 32)),
+        ]
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("field", (35, 40)), type_node("String", (42, 48)), (35, 48)
+            )
+        ]
+        assert definition.loc == (0, 50)
+
+    def simple_interface_inheriting_multiple_interfaces_with_leading_ampersand():
+        body = "interface Hello implements & Wo & rld { field: String }"
+        definition = assert_definitions(body, (0, 55))
+        assert isinstance(definition, InterfaceTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (10, 15))
+        assert definition.description is None
+        assert definition.interfaces == [
+            type_node("Wo", (29, 31)),
+            type_node("rld", (34, 37)),
+        ]
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("field", (40, 45)), type_node("String", (47, 53)), (40, 53)
+            )
+        ]
+        assert definition.loc == (0, 55)
+
+    def single_value_enum():
+        body = "enum Hello { WORLD }"
+        definition = assert_definitions(body, (0, 20))
+        assert isinstance(definition, EnumTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (5, 10))
+        assert definition.description is None
+        assert definition.directives == []
+        assert definition.values == [enum_value_node("WORLD", (13, 18))]
+        assert definition.loc == (0, 20)
+
+    def double_value_enum():
+        body = "enum Hello { WO, RLD }"
+        definition = assert_definitions(body, (0, 22))
+        assert isinstance(definition, EnumTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (5, 10))
+        assert definition.description is None
+        assert definition.directives == []
+        assert definition.values == [
+            enum_value_node("WO", (13, 15)),
+            enum_value_node("RLD", (17, 20)),
+        ]
+        assert definition.loc == (0, 22)
+
+    def simple_interface():
+        body = dedent(
+            """
+            interface Hello {
+              world: String
+            }
+            """
+        )
+        definition = assert_definitions(body, (0, 37))
+        assert isinstance(definition, InterfaceTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (11, 16))
+        assert definition.description is None
+        assert definition.interfaces == []
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node(
+                name_node("world", (21, 26)), type_node("String", (28, 34)), (21, 34)
+            )
+        ]
+        assert definition.loc == (1, 36)
+
+    def simple_field_with_arg():
+        body = dedent(
+            """
+            type Hello {
+              world(flag: Boolean): String
+            }
+            """
+        )
+        definition = assert_definitions(body, (0, 47))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.description is None
+        assert definition.interfaces == []
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node_with_args(
+                name_node("world", (16, 21)),
+                type_node("String", (38, 44)),
+                [
+                    input_value_node(
+                        name_node("flag", (22, 26)),
+                        type_node("Boolean", (28, 35)),
+                        None,
+                        (22, 35),
+                    )
+                ],
+                (16, 44),
+            )
+        ]
+        assert definition.loc == (1, 46)
+
+    def simple_field_with_arg_with_default_value():
+        body = dedent(
+            """
+            type Hello {
+              world(flag: Boolean = true): String
+            }
+            """
+        )
+        definition = assert_definitions(body, (0, 54))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.description is None
+        assert definition.interfaces == []
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node_with_args(
+                name_node("world", (16, 21)),
+                type_node("String", (45, 51)),
+                [
+                    input_value_node(
+                        name_node("flag", (22, 26)),
+                        type_node("Boolean", (28, 35)),
+                        boolean_value_node(True, (38, 42)),
+                        (22, 42),
+                    )
+                ],
+                (16, 51),
+            )
+        ]
+        assert definition.loc == (1, 53)
+
+    def simple_field_with_list_arg():
+        body = dedent(
+            """
+            type Hello {
+              world(things: [String]): String
+            }
+            """
+        )
+        definition = assert_definitions(body, (0, 50))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.description is None
+        assert definition.interfaces == []
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node_with_args(
+                name_node("world", (16, 21)),
+                type_node("String", (41, 47)),
+                [
+                    input_value_node(
+                        name_node("things", (22, 28)),
+                        list_type_node(type_node("String", (31, 37)), (30, 38)),
+                        None,
+                        (22, 38),
+                    )
+                ],
+                (16, 47),
+            )
+        ]
+        assert definition.loc == (1, 49)
+
+    def simple_field_with_two_args():
+        body = dedent(
+            """
+          type Hello {
+            world(argOne: Boolean, argTwo: Int): String
+          }
+          """
+        )
+        definition = assert_definitions(body, (0, 62))
+        assert isinstance(definition, ObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.description is None
+        assert definition.interfaces == []
+        assert definition.directives == []
+        assert definition.fields == [
+            field_node_with_args(
+                name_node("world", (16, 21)),
+                type_node("String", (53, 59)),
+                [
+                    input_value_node(
+                        name_node("argOne", (22, 28)),
+                        type_node("Boolean", (30, 37)),
+                        None,
+                        (22, 37),
+                    ),
+                    input_value_node(
+                        name_node("argTwo", (39, 45)),
+                        type_node("Int", (47, 50)),
+                        None,
+                        (39, 50),
+                    ),
+                ],
+                (16, 59),
+            )
+        ]
+        assert definition.loc == (1, 61)
+
+    def simple_union():
+        body = "union Hello = World"
+        definition = assert_definitions(body, (0, 19))
+        assert isinstance(definition, UnionTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.description is None
+        assert definition.directives == []
+        assert definition.types == [type_node("World", (14, 19))]
+        assert definition.loc == (0, 19)
+
+    def union_with_two_types():
+        body = "union Hello = Wo | Rld"
+        definition = assert_definitions(body, (0, 22))
+        assert isinstance(definition, UnionTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.description is None
+        assert definition.directives == []
+        assert definition.types == [
+            type_node("Wo", (14, 16)),
+            type_node("Rld", (19, 22)),
+        ]
+        assert definition.loc == (0, 22)
+
+    def union_with_two_types_and_leading_pipe():
+        body = "union Hello = | Wo | Rld"
+        definition = assert_definitions(body, (0, 24))
+        assert isinstance(definition, UnionTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (6, 11))
+        assert definition.directives == []
+        assert definition.types == [
+            type_node("Wo", (16, 18)),
+            type_node("Rld", (21, 24)),
+        ]
+        assert definition.loc == (0, 24)
+
+    def union_fails_with_no_types():
+        assert_syntax_error("union Hello = |", "Expected Name, found <EOF>.", (1, 16))
+
+    def union_fails_with_leading_double_pipe():
+        assert_syntax_error(
+            "union Hello = || Wo | Rld", "Expected Name, found '|'.", (1, 16)
+        )
+
+    def union_fails_with_double_pipe():
+        assert_syntax_error(
+            "union Hello = Wo || Rld", "Expected Name, found '|'.", (1, 19)
+        )
+
+    def union_fails_with_trailing_pipe():
+        assert_syntax_error(
+            "union Hello = | Wo | Rld |", "Expected Name, found <EOF>.", (1, 27)
+        )
+
+    def scalar():
+        body = "scalar Hello"
+        definition = assert_definitions(body, (0, 12))
+        assert isinstance(definition, ScalarTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (7, 12))
+        assert definition.description is None
+        assert definition.directives == []
+        assert definition.loc == (0, 12)
+
+    def simple_input_object():
+        body = "\ninput Hello {\n  world: String\n}"
+        definition = assert_definitions(body, (0, 32))
+        assert isinstance(definition, InputObjectTypeDefinitionNode)
+        assert definition.name == name_node("Hello", (7, 12))
+        assert definition.description is None
+        assert definition.directives == []
+        assert definition.fields == [
+            input_value_node(
+                name_node("world", (17, 22)),
+                type_node("String", (24, 30)),
+                None,
+                (17, 30),
+            )
+        ]
+        assert definition.loc == (1, 32)
+
+    def simple_input_object_with_args_should_fail():
+        assert_syntax_error(
+            "\ninput Hello {\n  world(foo : Int): String\n}",
+            "Expected ':', found '('.",
+            (3, 8),
+        )
+
+    def directive_definition():
+        body = "directive @foo on OBJECT | INTERFACE"
+        definition = assert_definitions(body, (0, 36))
+        assert isinstance(definition, DirectiveDefinitionNode)
+        assert definition.name == name_node("foo", (11, 14))
+        assert definition.description is None
+        assert definition.arguments == []
+        assert definition.repeatable is False
+        assert definition.locations == [
+            name_node("OBJECT", (18, 24)),
+            name_node("INTERFACE", (27, 36)),
+        ]
+
+    def repeatable_directive_definition():
+        body = "directive @foo repeatable on OBJECT | INTERFACE"
+        definition = assert_definitions(body, (0, 47))
+        assert isinstance(definition, DirectiveDefinitionNode)
+        assert definition.name == name_node("foo", (11, 14))
+        assert definition.description is None
+        assert definition.arguments == []
+        assert definition.repeatable is True
+        assert definition.locations == [
+            name_node("OBJECT", (29, 35)),
+            name_node("INTERFACE", (38, 47)),
+        ]
+
+    def directive_with_incorrect_locations():
+        assert_syntax_error(
+            "\ndirective @foo on FIELD | INCORRECT_LOCATION",
+            "Unexpected Name 'INCORRECT_LOCATION'.",
+            (2, 27),
+        )
+
+    def parses_kitchen_sink_schema(kitchen_sink_sdl):  # noqa: F811
+        assert parse(kitchen_sink_sdl)
+
+    def disallow_legacy_sdl_empty_fields_supports_type_with_empty_fields():
+        assert_syntax_error("type Hello { }", "Expected Name, found '}'.", (1, 14))
+
+    def disallow_legacy_sdl_implements_interfaces():
+        assert_syntax_error(
+            "type Hello implements Wo rld { field: String }",
+            "Unexpected Name 'rld'.",
+            (1, 26),
+        )
diff --git a/tests/language/test_schema_printer.py b/tests/language/test_schema_printer.py
new file mode 100644
index 0000000..733f2ad
--- /dev/null
+++ b/tests/language/test_schema_printer.py
@@ -0,0 +1,174 @@
+from copy import deepcopy
+
+from pytest import raises  # type: ignore
+
+from graphql.language import ScalarTypeDefinitionNode, NameNode, print_ast, parse
+
+from ..fixtures import kitchen_sink_sdl  # noqa: F401
+from ..utils import dedent
+
+
+def describe_printer_sdl_document():
+    def prints_minimal_ast():
+        node = ScalarTypeDefinitionNode(name=NameNode(value="foo"))
+        assert print_ast(node) == "scalar foo"
+
+    def produces_helpful_error_messages():
+        bad_ast = {"random": "Data"}
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            print_ast(bad_ast)  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "Not an AST Node: {'random': 'Data'}."
+
+    # noinspection PyShadowingNames
+    def does_not_alter_ast(kitchen_sink_sdl):  # noqa: F811
+        ast = parse(kitchen_sink_sdl)
+        ast_copy = deepcopy(ast)
+        print_ast(ast)
+        assert ast == ast_copy
+
+    # noinspection PyShadowingNames
+    def prints_kitchen_sink(kitchen_sink_sdl):  # noqa: F811
+        ast = parse(kitchen_sink_sdl)
+        printed = print_ast(ast)
+
+        assert printed == dedent(
+            '''
+            """This is a description of the schema as a whole."""
+            schema {
+              query: QueryType
+              mutation: MutationType
+            }
+
+            """
+            This is a description
+            of the `Foo` type.
+            """
+            type Foo implements Bar & Baz & Two {
+              "Description of the `one` field."
+              one: Type
+              """This is a description of the `two` field."""
+              two(
+                """This is a description of the `argument` argument."""
+                argument: InputType!
+              ): Type
+              """This is a description of the `three` field."""
+              three(argument: InputType, other: String): Int
+              four(argument: String = "string"): String
+              five(argument: [String] = ["string", "string"]): String
+              six(argument: InputType = {key: "value"}): Type
+              seven(argument: Int = null): Type
+            }
+
+            type AnnotatedObject @onObject(arg: "value") {
+              annotatedField(arg: Type = "default" @onArgumentDefinition): Type @onField
+            }
+
+            type UndefinedType
+
+            extend type Foo {
+              seven(argument: [String]): Type
+            }
+
+            extend type Foo @onType
+
+            interface Bar {
+              one: Type
+              four(argument: String = "string"): String
+            }
+
+            interface AnnotatedInterface @onInterface {
+              annotatedField(arg: Type @onArgumentDefinition): Type @onField
+            }
+
+            interface UndefinedInterface
+
+            extend interface Bar implements Two {
+              two(argument: InputType!): Type
+            }
+
+            extend interface Bar @onInterface
+
+            interface Baz implements Bar & Two {
+              one: Type
+              two(argument: InputType!): Type
+              four(argument: String = "string"): String
+            }
+
+            union Feed = Story | Article | Advert
+
+            union AnnotatedUnion @onUnion = A | B
+
+            union AnnotatedUnionTwo @onUnion = A | B
+
+            union UndefinedUnion
+
+            extend union Feed = Photo | Video
+
+            extend union Feed @onUnion
+
+            scalar CustomScalar
+
+            scalar AnnotatedScalar @onScalar
+
+            extend scalar CustomScalar @onScalar
+
+            enum Site {
+              """This is a description of the `DESKTOP` value"""
+              DESKTOP
+              """This is a description of the `MOBILE` value"""
+              MOBILE
+              "This is a description of the `WEB` value"
+              WEB
+            }
+
+            enum AnnotatedEnum @onEnum {
+              ANNOTATED_VALUE @onEnumValue
+              OTHER_VALUE
+            }
+
+            enum UndefinedEnum
+
+            extend enum Site {
+              VR
+            }
+
+            extend enum Site @onEnum
+
+            input InputType {
+              key: String!
+              answer: Int = 42
+            }
+
+            input AnnotatedInput @onInputObject {
+              annotatedField: Type @onInputFieldDefinition
+            }
+
+            input UndefinedInput
+
+            extend input InputType {
+              other: Float = 1.23e4 @onInputFieldDefinition
+            }
+
+            extend input InputType @onInputObject
+
+            """This is a description of the `@skip` directive"""
+            directive @skip(
+              """This is a description of the `if` argument"""
+              if: Boolean! @onArgumentDefinition
+            ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+            directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+            directive @include2(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+            directive @myRepeatableDir(name: String!) repeatable on OBJECT | INTERFACE
+
+            extend schema @onSchema
+
+            extend schema @onSchema {
+              subscription: SubscriptionType
+            }
+            '''  # noqa: E501
+        )
diff --git a/tests/language/test_source.py b/tests/language/test_source.py
new file mode 100644
index 0000000..e3ba513
--- /dev/null
+++ b/tests/language/test_source.py
@@ -0,0 +1,126 @@
+import weakref
+
+from pytest import raises  # type: ignore
+
+from graphql.language import Source, SourceLocation
+
+from ..utils import dedent
+
+
+def describe_source():
+    def accepts_body_and_name():
+        source = Source("foo", "bar")
+        assert source.body == "foo"
+        assert source.name == "bar"
+
+    def accepts_location_offset():
+        location_offset = SourceLocation(2, 3)
+        source = Source("", "", location_offset)
+        assert source.location_offset is location_offset
+
+    def accepts_tuple_as_location_offset():
+        # noinspection PyTypeChecker
+        source = Source("", "", (2, 3))  # type: ignore
+        assert isinstance(source.location_offset, SourceLocation)
+        assert source.location_offset == (2, 3)
+
+    def uses_default_arguments():
+        source = Source("")
+        assert source.name == "GraphQL request"
+        assert isinstance(source.location_offset, SourceLocation)
+        assert source.location_offset == (1, 1)
+
+    def can_get_location():
+        body = dedent(
+            """
+            line 1
+            line 2
+            line 3
+            """
+        )
+        source = Source(body)
+        assert source.body == body
+        location = source.get_location(body.find("2"))
+        assert isinstance(location, SourceLocation)
+        assert location == (2, 6)
+
+    def can_be_stringified():
+        source = Source("")
+        assert str(source) == "<Source name='GraphQL request'>"
+
+        source = Source("", "Custom source name")
+        assert str(source) == "<Source name='Custom source name'>"
+
+    def can_be_compared():
+        source = Source("foo")
+        assert source == source
+        assert not source != source
+        assert source == "foo"
+        assert not source != "foo"
+        same_source = Source("foo")
+        assert source == same_source
+        assert not source != same_source
+        different_source = Source("bar")
+        assert not source == different_source
+        assert source != different_source
+        assert not source == "bar"
+        assert source != "bar"
+
+    def can_create_weak_reference():
+        source = Source("foo")
+        ref = weakref.ref(source)
+        assert ref() is source
+
+    def can_create_custom_attribute():
+        node = Source("foo")
+        node.custom = "bar"  # type: ignore
+        assert node.custom == "bar"  # type: ignore
+
+    def rejects_invalid_body_and_name():
+        with raises(TypeError, match="body must be a string\\."):
+            # noinspection PyTypeChecker
+            Source(None)  # type: ignore
+        with raises(TypeError, match="body must be a string\\."):
+            # noinspection PyTypeChecker
+            Source(1)  # type: ignore
+        with raises(TypeError, match="name must be a string\\."):
+            # noinspection PyTypeChecker
+            Source("", None)  # type: ignore
+        with raises(TypeError, match="name must be a string\\."):
+            # noinspection PyTypeChecker
+            Source("", 1)  # type: ignore
+
+    def rejects_invalid_location_offset():
+        def create_source(location_offset):
+            return Source("", "", location_offset)
+
+        with raises(TypeError):
+            create_source(None)
+        with raises(TypeError):
+            create_source(1)
+        with raises(TypeError):
+            create_source((1,))
+        with raises(TypeError):
+            create_source((1, 2, 3))
+
+        with raises(
+            ValueError,
+            match="line in location_offset is 1-indexed and must be positive\\.",
+        ):
+            create_source((0, 1))
+        with raises(
+            ValueError,
+            match="line in location_offset is 1-indexed and must be positive\\.",
+        ):
+            create_source((-1, 1))
+
+        with raises(
+            ValueError,
+            match="column in location_offset is 1-indexed and must be positive\\.",
+        ):
+            create_source((1, 0))
+        with raises(
+            ValueError,
+            match="column in location_offset is 1-indexed and must be positive\\.",
+        ):
+            create_source((1, -1))
diff --git a/tests/language/test_visitor.py b/tests/language/test_visitor.py
new file mode 100644
index 0000000..0fd7223
--- /dev/null
+++ b/tests/language/test_visitor.py
@@ -0,0 +1,1505 @@
+from copy import copy
+from functools import partial
+from typing import cast, Dict, List, Optional, Tuple
+
+from pytest import mark, raises  # type: ignore
+
+from graphql.language import (
+    Node,
+    FieldNode,
+    NameNode,
+    SelectionNode,
+    SelectionSetNode,
+    parse,
+    visit,
+    BREAK,
+    REMOVE,
+    SKIP,
+    ParallelVisitor,
+    Visitor,
+)
+from graphql.language.visitor import QUERY_DOCUMENT_KEYS
+from graphql.pyutils import FrozenList
+
+from ..fixtures import kitchen_sink_query  # noqa: F401
+
+
+def check_visitor_fn_args(ast, node, key, parent, path, ancestors, is_edited=False):
+    assert isinstance(node, Node)
+
+    is_root = key is None
+    if is_root:
+        if not is_edited:
+            assert node is ast
+        assert parent is None
+        assert path == []
+        assert ancestors == []
+        return
+
+    assert isinstance(key, (int, str))
+
+    if isinstance(key, int):
+        assert isinstance(parent, list)
+        assert 0 <= key <= len(parent)
+    else:
+        assert isinstance(parent, Node)
+        assert hasattr(parent, key)
+
+    assert isinstance(path, list)
+    assert path[-1] == key
+
+    assert isinstance(ancestors, list)
+    assert len(ancestors) == len(path) - 1
+
+    if not is_edited:
+        current_node = ast
+
+        for i, ancestor in enumerate(ancestors):
+            assert ancestor is current_node
+            k = path[i]
+            assert isinstance(k, (int, str))
+            if isinstance(k, int):
+                assert isinstance(current_node, list)
+                assert 0 <= k <= len(current_node)
+                current_node = current_node[k]
+            else:
+                assert isinstance(current_node, Node)
+                assert hasattr(current_node, k)
+                current_node = getattr(current_node, k)
+            assert current_node is not None
+
+        assert parent is current_node
+        if isinstance(key, int):
+            assert parent[key] is node
+        else:
+            assert getattr(parent, key) is node
+
+
+check_visitor_fn_args_edited = partial(check_visitor_fn_args, is_edited=True)
+
+
+def get_value(node):
+    return getattr(node, "value", None)
+
+
+def describe_visitor():
+    def visit_with_invalid_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            visit("invalid", Visitor())  # type: ignore
+        assert str(exc_info.value) == "Not an AST Node: 'invalid'."
+
+    def visit_with_invalid_visitor():
+        ast = parse("{ a }", no_location=True)
+
+        class TestVisitor:
+            def enter(self, *_args):
+                pass
+
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            visit(ast, TestVisitor())  # type: ignore
+        assert str(exc_info.value) == "Not an AST Visitor: <TestVisitor instance>."
+
+    def visitors_support_all_method_variants():
+        class TestVisitorWithInstanceMethods(Visitor):
+            def enter(self, node, *args):
+                assert isinstance(self, TestVisitorWithInstanceMethods)
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"enter:{node.kind}")
+                pass
+
+            def leave(self, node, *args):
+                assert isinstance(self, TestVisitorWithInstanceMethods)
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"leave:{node.kind}")
+                pass
+
+            def enter_field(self, node, *args):
+                assert isinstance(self, TestVisitorWithInstanceMethods)
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"enter_field:{node.kind}")
+                pass
+
+            def leave_field(self, node, *args):
+                assert isinstance(self, TestVisitorWithInstanceMethods)
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"leave_field:{node.kind}")
+                pass
+
+        class TestVisitorWithClassMethods(Visitor):
+            @classmethod
+            def enter(cls, node, *args):
+                assert cls is TestVisitorWithClassMethods
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"enter:{node.kind}")
+                pass
+
+            @classmethod
+            def leave(cls, node, *args):
+                assert cls is TestVisitorWithClassMethods
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"leave:{node.kind}")
+                pass
+
+            @classmethod
+            def enter_field(cls, node, *args):
+                assert cls is TestVisitorWithClassMethods
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"enter_field:{node.kind}")
+                pass
+
+            @classmethod
+            def leave_field(cls, node, *args):
+                assert cls is TestVisitorWithClassMethods
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"leave_field:{node.kind}")
+                pass
+
+        class TestVisitorWithStaticMethods(Visitor):
+            @staticmethod
+            def enter(node, *args):
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"enter:{node.kind}")
+                pass
+
+            @staticmethod
+            def leave(node, *args):
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"leave:{node.kind}")
+                pass
+
+            @staticmethod
+            def enter_field(node, *args):
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"enter_field:{node.kind}")
+                pass
+
+            @staticmethod
+            def leave_field(node, *args):
+                assert isinstance(node, Node)
+                assert len(args) == 4
+                visited.append(f"leave_field:{node.kind}")
+                pass
+
+        for visitor_class in (
+            TestVisitorWithInstanceMethods,
+            TestVisitorWithClassMethods,
+            TestVisitorWithStaticMethods,
+        ):
+            ast = parse("{ a }")
+            visited: List[str] = []
+            visit(ast, visitor_class())
+            assert visited == [
+                "enter:document",
+                "enter:operation_definition",
+                "enter:selection_set",
+                "enter_field:field",
+                "enter:name",
+                "leave:name",
+                "leave_field:field",
+                "leave:selection_set",
+                "leave:operation_definition",
+                "leave:document",
+            ]
+
+    def validates_path_argument():
+        ast = parse("{ a }", no_location=True)
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                visited.append(["enter", *args[3]])
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args(ast, *args)
+                visited.append(["leave", *args[3]])
+
+        visit(ast, TestVisitor())
+        assert visited == [
+            ["enter"],
+            ["enter", "definitions", 0],
+            ["enter", "definitions", 0, "selection_set"],
+            ["enter", "definitions", 0, "selection_set", "selections", 0],
+            ["enter", "definitions", 0, "selection_set", "selections", 0, "name"],
+            ["leave", "definitions", 0, "selection_set", "selections", 0, "name"],
+            ["leave", "definitions", 0, "selection_set", "selections", 0],
+            ["leave", "definitions", 0, "selection_set"],
+            ["leave", "definitions", 0],
+            ["leave"],
+        ]
+
+    def validates_ancestors_argument():
+        ast = parse("{ a }", no_location=True)
+        visited_nodes = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(node, key, parent, _path, ancestors):
+                in_array = isinstance(key, int)
+                if in_array:
+                    visited_nodes.append(parent)
+                visited_nodes.append(node)
+                expected_ancestors = visited_nodes[0:-2]
+                assert ancestors == expected_ancestors
+
+            @staticmethod
+            def leave(_node, key, _parent, _path, ancestors):
+                expected_ancestors = visited_nodes[0:-2]
+                assert ancestors == expected_ancestors
+                in_array = isinstance(key, int)
+                if in_array:
+                    visited_nodes.pop()
+                visited_nodes.pop()
+
+        visit(ast, TestVisitor())
+
+    def allows_visiting_only_specified_nodes():
+        ast = parse("{ a }", no_location=True)
+        visited = []
+
+        class TestVisitor(Visitor):
+            selection_set = None
+
+            @staticmethod
+            def enter_field(node, *_args):
+                visited.append(["enter", node.kind])
+
+            @staticmethod
+            def leave_field(node, *_args):
+                visited.append(["leave", node.kind])
+
+        visit(ast, TestVisitor())
+        assert visited == [["enter", "field"], ["leave", "field"]]
+
+    def allows_editing_a_node_both_on_enter_and_on_leave():
+        ast = parse("{ a, b, c { a, b, c } }", no_location=True)
+        visited = []
+
+        class TestVisitor(Visitor):
+            selection_set = None
+
+            def enter_operation_definition(self, *args):
+                check_visitor_fn_args(ast, *args)
+                node = copy(args[0])
+                assert len(node.selection_set.selections) == 3
+                self.selection_set = node.selection_set
+                node.selection_set = SelectionSetNode(selections=[])
+                visited.append("enter")
+                return node
+
+            def leave_operation_definition(self, *args):
+                check_visitor_fn_args_edited(ast, *args)
+                node = copy(args[0])
+                assert not node.selection_set.selections
+                node.selection_set = self.selection_set
+                visited.append("leave")
+                return node
+
+        edited_ast = visit(ast, TestVisitor())
+        assert edited_ast == ast
+        assert visited == ["enter", "leave"]
+
+    @mark.parametrize("remove_action", (REMOVE, Ellipsis), ids=("REMOVE", "Ellipsis"))
+    def allows_for_editing_on_enter(remove_action):
+        ast = parse("{ a, b, c { a, b, c } }", no_location=True)
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                if isinstance(node, FieldNode) and node.name.value == "b":
+                    return remove_action
+
+        edited_ast = visit(ast, TestVisitor())
+        assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
+        assert edited_ast == parse("{ a,    c { a,    c } }", no_location=True)
+
+    @mark.parametrize("remove_action", (REMOVE, Ellipsis), ids=("REMOVE", "Ellipsis"))
+    def allows_for_editing_on_leave(remove_action):
+        ast = parse("{ a, b, c { a, b, c } }", no_location=True)
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args_edited(ast, *args)
+                node = args[0]
+                if isinstance(node, FieldNode) and node.name.value == "b":
+                    return remove_action
+
+        edited_ast = visit(ast, TestVisitor())
+        assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
+        assert edited_ast == parse("{ a,    c { a,    c } }", no_location=True)
+
+    @mark.parametrize("skip_action", (SKIP, False), ids=("SKIP", "False"))
+    def ignores_false_returned_on_leave(skip_action):
+        ast = parse("{ a, b, c { a, b, c } }", no_location=True)
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def leave(*args):
+                return skip_action
+
+        returned_ast = visit(ast, TestVisitor())
+        assert returned_ast == parse("{ a, b, c { a, b, c } }", no_location=True)
+
+    def visits_edited_node():
+        ast = parse("{ a { x } }", no_location=True)
+        added_field = FieldNode(name=NameNode(value="__typename"))
+
+        class TestVisitor(Visitor):
+            did_visit_added_field = False
+
+            def enter(self, *args):
+                check_visitor_fn_args_edited(ast, *args)
+                node = args[0]
+                if isinstance(node, FieldNode) and node.name.value == "a":
+                    node = copy(node)
+                    assert node.selection_set
+                    node.selection_set.selections = (
+                        FrozenList([added_field]) + node.selection_set.selections
+                    )
+                    return node
+                if node == added_field:
+                    self.did_visit_added_field = True
+
+        visitor = TestVisitor()
+        visit(ast, visitor)
+        assert visitor.did_visit_added_field
+
+    @mark.parametrize("skip_action", (SKIP, False), ids=("SKIP", "False"))
+    def allows_skipping_a_sub_tree(skip_action):
+        ast = parse("{ a, b { x }, c }", no_location=True)
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+                if kind == "field" and node.name.value == "b":
+                    return skip_action
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+
+        visit(ast, TestVisitor())
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "c"],
+            ["leave", "name", "c"],
+            ["leave", "field", None],
+            ["leave", "selection_set", None],
+            ["leave", "operation_definition", None],
+            ["leave", "document", None],
+        ]
+
+    @mark.parametrize("break_action", (BREAK, True), ids=("BREAK", "True"))
+    def allows_early_exit_while_visiting(break_action):
+        ast = parse("{ a, b { x }, c }", no_location=True)
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+                if kind == "name" and node.value == "x":
+                    return break_action
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+
+        visit(ast, TestVisitor())
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "b"],
+            ["leave", "name", "b"],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "x"],
+        ]
+
+    @mark.parametrize("break_action", (BREAK, True), ids=("BREAK", "True"))
+    def allows_early_exit_while_leaving(break_action):
+        ast = parse("{ a, b { x }, c }", no_location=True)
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+                if kind == "name" and node.value == "x":
+                    return break_action
+
+        visit(ast, TestVisitor())
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "b"],
+            ["leave", "name", "b"],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "x"],
+            ["leave", "name", "x"],
+        ]
+
+    def allows_a_named_functions_visitor_api():
+        ast = parse("{ a, b { x }, c }", no_location=True)
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter_name(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+
+            @staticmethod
+            def enter_selection_set(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+
+            @staticmethod
+            def leave_selection_set(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+
+        visit(ast, TestVisitor())
+        assert visited == [
+            ["enter", "selection_set", None],
+            ["enter", "name", "a"],
+            ["enter", "name", "b"],
+            ["enter", "selection_set", None],
+            ["enter", "name", "x"],
+            ["leave", "selection_set", None],
+            ["enter", "name", "c"],
+            ["leave", "selection_set", None],
+        ]
+
+    def experimental_visits_variables_defined_in_fragments():
+        ast = parse(
+            "fragment a($v: Boolean = false) on t { f }",
+            no_location=True,
+            experimental_fragment_variables=True,
+        )
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+
+        visit(ast, TestVisitor())
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "fragment_definition", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["enter", "variable_definition", None],
+            ["enter", "variable", None],
+            ["enter", "name", "v"],
+            ["leave", "name", "v"],
+            ["leave", "variable", None],
+            ["enter", "named_type", None],
+            ["enter", "name", "Boolean"],
+            ["leave", "name", "Boolean"],
+            ["leave", "named_type", None],
+            ["enter", "boolean_value", False],
+            ["leave", "boolean_value", False],
+            ["leave", "variable_definition", None],
+            ["enter", "named_type", None],
+            ["enter", "name", "t"],
+            ["leave", "name", "t"],
+            ["leave", "named_type", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "f"],
+            ["leave", "name", "f"],
+            ["leave", "field", None],
+            ["leave", "selection_set", None],
+            ["leave", "fragment_definition", None],
+            ["leave", "document", None],
+        ]
+
+    # noinspection PyShadowingNames
+    def visits_kitchen_sink(kitchen_sink_query):  # noqa: F811
+        ast = parse(kitchen_sink_query)
+        visited: List = []
+        record = visited.append
+        arg_stack: List = []
+        push = arg_stack.append
+        pop = arg_stack.pop
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                node, key, parent = args[:3]
+                parent_kind = parent.kind if isinstance(parent, Node) else None
+                record(["enter", node.kind, key, parent_kind])
+
+                check_visitor_fn_args(ast, *args)
+                push(args[:])
+
+            @staticmethod
+            def leave(*args):
+                node, key, parent = args[:3]
+                parent_kind = parent.kind if isinstance(parent, Node) else None
+                record(["leave", node.kind, key, parent_kind])
+
+                assert pop() == args
+
+        visit(ast, TestVisitor())
+
+        assert arg_stack == []
+        assert visited == [
+            ["enter", "document", None, None],
+            ["enter", "operation_definition", 0, None],
+            ["enter", "name", "name", "operation_definition"],
+            ["leave", "name", "name", "operation_definition"],
+            ["enter", "variable_definition", 0, None],
+            ["enter", "variable", "variable", "variable_definition"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "variable", "variable_definition"],
+            ["enter", "named_type", "type", "variable_definition"],
+            ["enter", "name", "name", "named_type"],
+            ["leave", "name", "name", "named_type"],
+            ["leave", "named_type", "type", "variable_definition"],
+            ["leave", "variable_definition", 0, None],
+            ["enter", "variable_definition", 1, None],
+            ["enter", "variable", "variable", "variable_definition"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "variable", "variable_definition"],
+            ["enter", "named_type", "type", "variable_definition"],
+            ["enter", "name", "name", "named_type"],
+            ["leave", "name", "name", "named_type"],
+            ["leave", "named_type", "type", "variable_definition"],
+            ["enter", "enum_value", "default_value", "variable_definition"],
+            ["leave", "enum_value", "default_value", "variable_definition"],
+            ["leave", "variable_definition", 1, None],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["leave", "directive", 0, None],
+            ["enter", "selection_set", "selection_set", "operation_definition"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "alias", "field"],
+            ["leave", "name", "alias", "field"],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "argument", 0, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "list_value", "value", "argument"],
+            ["enter", "int_value", 0, None],
+            ["leave", "int_value", 0, None],
+            ["enter", "int_value", 1, None],
+            ["leave", "int_value", 1, None],
+            ["leave", "list_value", "value", "argument"],
+            ["leave", "argument", 0, None],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 0, None],
+            ["enter", "inline_fragment", 1, None],
+            ["enter", "named_type", "type_condition", "inline_fragment"],
+            ["enter", "name", "name", "named_type"],
+            ["leave", "name", "name", "named_type"],
+            ["leave", "named_type", "type_condition", "inline_fragment"],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["leave", "directive", 0, None],
+            ["enter", "selection_set", "selection_set", "inline_fragment"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 0, None],
+            ["enter", "field", 1, None],
+            ["enter", "name", "alias", "field"],
+            ["leave", "name", "alias", "field"],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "argument", 0, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "int_value", "value", "argument"],
+            ["leave", "int_value", "value", "argument"],
+            ["leave", "argument", 0, None],
+            ["enter", "argument", 1, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "variable", "value", "argument"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "value", "argument"],
+            ["leave", "argument", 1, None],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["enter", "argument", 0, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "variable", "value", "argument"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "value", "argument"],
+            ["leave", "argument", 0, None],
+            ["leave", "directive", 0, None],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 0, None],
+            ["enter", "fragment_spread", 1, None],
+            ["enter", "name", "name", "fragment_spread"],
+            ["leave", "name", "name", "fragment_spread"],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["leave", "directive", 0, None],
+            ["leave", "fragment_spread", 1, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 1, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "inline_fragment"],
+            ["leave", "inline_fragment", 1, None],
+            ["enter", "inline_fragment", 2, None],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["enter", "argument", 0, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "variable", "value", "argument"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "value", "argument"],
+            ["leave", "argument", 0, None],
+            ["leave", "directive", 0, None],
+            ["enter", "selection_set", "selection_set", "inline_fragment"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "inline_fragment"],
+            ["leave", "inline_fragment", 2, None],
+            ["enter", "inline_fragment", 3, None],
+            ["enter", "selection_set", "selection_set", "inline_fragment"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "inline_fragment"],
+            ["leave", "inline_fragment", 3, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "operation_definition"],
+            ["leave", "operation_definition", 0, None],
+            ["enter", "operation_definition", 1, None],
+            ["enter", "name", "name", "operation_definition"],
+            ["leave", "name", "name", "operation_definition"],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["leave", "directive", 0, None],
+            ["enter", "selection_set", "selection_set", "operation_definition"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "argument", 0, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "int_value", "value", "argument"],
+            ["leave", "int_value", "value", "argument"],
+            ["leave", "argument", 0, None],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["leave", "directive", 0, None],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["leave", "directive", 0, None],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "operation_definition"],
+            ["leave", "operation_definition", 1, None],
+            ["enter", "operation_definition", 2, None],
+            ["enter", "name", "name", "operation_definition"],
+            ["leave", "name", "name", "operation_definition"],
+            ["enter", "variable_definition", 0, None],
+            ["enter", "variable", "variable", "variable_definition"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "variable", "variable_definition"],
+            ["enter", "named_type", "type", "variable_definition"],
+            ["enter", "name", "name", "named_type"],
+            ["leave", "name", "name", "named_type"],
+            ["leave", "named_type", "type", "variable_definition"],
+            ["leave", "variable_definition", 0, None],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["leave", "directive", 0, None],
+            ["enter", "selection_set", "selection_set", "operation_definition"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "argument", 0, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "variable", "value", "argument"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "value", "argument"],
+            ["leave", "argument", 0, None],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 0, None],
+            ["enter", "field", 1, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "selection_set", "selection_set", "field"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 1, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "operation_definition"],
+            ["leave", "operation_definition", 2, None],
+            ["enter", "fragment_definition", 3, None],
+            ["enter", "name", "name", "fragment_definition"],
+            ["leave", "name", "name", "fragment_definition"],
+            ["enter", "named_type", "type_condition", "fragment_definition"],
+            ["enter", "name", "name", "named_type"],
+            ["leave", "name", "name", "named_type"],
+            ["leave", "named_type", "type_condition", "fragment_definition"],
+            ["enter", "directive", 0, None],
+            ["enter", "name", "name", "directive"],
+            ["leave", "name", "name", "directive"],
+            ["leave", "directive", 0, None],
+            ["enter", "selection_set", "selection_set", "fragment_definition"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "argument", 0, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "variable", "value", "argument"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "value", "argument"],
+            ["leave", "argument", 0, None],
+            ["enter", "argument", 1, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "variable", "value", "argument"],
+            ["enter", "name", "name", "variable"],
+            ["leave", "name", "name", "variable"],
+            ["leave", "variable", "value", "argument"],
+            ["leave", "argument", 1, None],
+            ["enter", "argument", 2, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "object_value", "value", "argument"],
+            ["enter", "object_field", 0, None],
+            ["enter", "name", "name", "object_field"],
+            ["leave", "name", "name", "object_field"],
+            ["enter", "string_value", "value", "object_field"],
+            ["leave", "string_value", "value", "object_field"],
+            ["leave", "object_field", 0, None],
+            ["enter", "object_field", 1, None],
+            ["enter", "name", "name", "object_field"],
+            ["leave", "name", "name", "object_field"],
+            ["enter", "string_value", "value", "object_field"],
+            ["leave", "string_value", "value", "object_field"],
+            ["leave", "object_field", 1, None],
+            ["leave", "object_value", "value", "argument"],
+            ["leave", "argument", 2, None],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "fragment_definition"],
+            ["leave", "fragment_definition", 3, None],
+            ["enter", "operation_definition", 4, None],
+            ["enter", "selection_set", "selection_set", "operation_definition"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["enter", "argument", 0, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "boolean_value", "value", "argument"],
+            ["leave", "boolean_value", "value", "argument"],
+            ["leave", "argument", 0, None],
+            ["enter", "argument", 1, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "boolean_value", "value", "argument"],
+            ["leave", "boolean_value", "value", "argument"],
+            ["leave", "argument", 1, None],
+            ["enter", "argument", 2, None],
+            ["enter", "name", "name", "argument"],
+            ["leave", "name", "name", "argument"],
+            ["enter", "null_value", "value", "argument"],
+            ["leave", "null_value", "value", "argument"],
+            ["leave", "argument", 2, None],
+            ["leave", "field", 0, None],
+            ["enter", "field", 1, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 1, None],
+            ["leave", "selection_set", "selection_set", "operation_definition"],
+            ["leave", "operation_definition", 4, None],
+            ["enter", "operation_definition", 5, None],
+            ["enter", "selection_set", "selection_set", "operation_definition"],
+            ["enter", "field", 0, None],
+            ["enter", "name", "name", "field"],
+            ["leave", "name", "name", "field"],
+            ["leave", "field", 0, None],
+            ["leave", "selection_set", "selection_set", "operation_definition"],
+            ["leave", "operation_definition", 5, None],
+            ["leave", "document", None, None],
+        ]
+
+
+def describe_support_for_custom_ast_nodes():
+    custom_ast = parse("{ a }")
+
+    class CustomFieldNode(SelectionNode):
+        __slots__ = "name", "selection_set"
+
+        name: NameNode
+        selection_set: Optional[SelectionSetNode]
+
+    custom_selection_set = cast(FieldNode, custom_ast.definitions[0]).selection_set
+    assert custom_selection_set is not None
+    custom_selection_set.selections = custom_selection_set.selections + [
+        CustomFieldNode(
+            name=NameNode(value="b"),
+            selection_set=SelectionSetNode(
+                selections=CustomFieldNode(name=NameNode(value="c"))
+            ),
+        )
+    ]
+
+    def does_not_traverse_unknown_node_kinds():
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(node, *_args):
+                visited.append(["enter", node.kind, get_value(node)])
+
+            @staticmethod
+            def leave(node, *_args):
+                visited.append(["leave", node.kind, get_value(node)])
+
+        visit(custom_ast, TestVisitor())
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "custom_field", None],
+            ["leave", "custom_field", None],
+            ["leave", "selection_set", None],
+            ["leave", "operation_definition", None],
+            ["leave", "document", None],
+        ]
+
+    def does_traverse_unknown_node_with_visitor_keys():
+        custom_query_document_keys: Dict[str, Tuple[str, ...]] = {
+            **QUERY_DOCUMENT_KEYS,
+            "custom_field": ("name", "selection_set"),
+        }
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(node, *_args):
+                visited.append(["enter", node.kind, get_value(node)])
+
+            @staticmethod
+            def leave(node, *_args):
+                visited.append(["leave", node.kind, get_value(node)])
+
+        visit(custom_ast, TestVisitor(), custom_query_document_keys)
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "custom_field", None],
+            ["enter", "name", "b"],
+            ["leave", "name", "b"],
+            ["enter", "selection_set", None],
+            ["enter", "custom_field", None],
+            ["enter", "name", "c"],
+            ["leave", "name", "c"],
+            ["leave", "custom_field", None],
+            ["leave", "selection_set", None],
+            ["leave", "custom_field", None],
+            ["leave", "selection_set", None],
+            ["leave", "operation_definition", None],
+            ["leave", "document", None],
+        ]
+
+    def cannot_define_visitor_with_unknown_ast_nodes():
+        with raises(TypeError) as exc_info:
+
+            class VisitorWithNonExistingNode(Visitor):
+                def enter_field(self, *_args):
+                    pass
+
+                def leave_garfield(self, *_args):
+                    pass
+
+        assert str(exc_info.value) == "Invalid AST node kind: garfield."
+
+        with raises(TypeError) as exc_info:
+
+            class VisitorWithUnspecificNode(Visitor):
+                def enter_type_system_extension(self, *_args):
+                    pass
+
+        assert str(exc_info.value) == "Invalid AST node kind: type_system_extension."
+
+
+def describe_visit_in_parallel():
+    @mark.parametrize("skip_action", (SKIP, False), ids=("SKIP", "False"))
+    def allows_skipping_a_sub_tree(skip_action):
+        # Note: nearly identical to the above test but using ParallelVisitor
+        ast = parse("{ a, b { x }, c }")
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+                if kind == "field" and node.name.value == "b":
+                    return skip_action
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+
+        visit(ast, ParallelVisitor([TestVisitor()]))
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "c"],
+            ["leave", "name", "c"],
+            ["leave", "field", None],
+            ["leave", "selection_set", None],
+            ["leave", "operation_definition", None],
+            ["leave", "document", None],
+        ]
+
+    @mark.parametrize("skip_action", (SKIP, False), ids=("SKIP", "False"))
+    def allows_skipping_different_sub_trees(skip_action):
+        ast = parse("{ a { x }, b { y} }")
+        visited = []
+
+        class TestVisitor(Visitor):
+            def __init__(self, name):
+                self.name = name
+
+            def enter(self, *args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                name = self.name
+                visited.append([f"no-{name}", "enter", kind, value])
+                if kind == "field" and node.name.value == name:
+                    return skip_action
+
+            def leave(self, *args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                name = self.name
+                visited.append([f"no-{name}", "leave", kind, value])
+
+        visit(ast, ParallelVisitor([TestVisitor("a"), TestVisitor("b")]))
+        assert visited == [
+            ["no-a", "enter", "document", None],
+            ["no-b", "enter", "document", None],
+            ["no-a", "enter", "operation_definition", None],
+            ["no-b", "enter", "operation_definition", None],
+            ["no-a", "enter", "selection_set", None],
+            ["no-b", "enter", "selection_set", None],
+            ["no-a", "enter", "field", None],
+            ["no-b", "enter", "field", None],
+            ["no-b", "enter", "name", "a"],
+            ["no-b", "leave", "name", "a"],
+            ["no-b", "enter", "selection_set", None],
+            ["no-b", "enter", "field", None],
+            ["no-b", "enter", "name", "x"],
+            ["no-b", "leave", "name", "x"],
+            ["no-b", "leave", "field", None],
+            ["no-b", "leave", "selection_set", None],
+            ["no-b", "leave", "field", None],
+            ["no-a", "enter", "field", None],
+            ["no-b", "enter", "field", None],
+            ["no-a", "enter", "name", "b"],
+            ["no-a", "leave", "name", "b"],
+            ["no-a", "enter", "selection_set", None],
+            ["no-a", "enter", "field", None],
+            ["no-a", "enter", "name", "y"],
+            ["no-a", "leave", "name", "y"],
+            ["no-a", "leave", "field", None],
+            ["no-a", "leave", "selection_set", None],
+            ["no-a", "leave", "field", None],
+            ["no-a", "leave", "selection_set", None],
+            ["no-b", "leave", "selection_set", None],
+            ["no-a", "leave", "operation_definition", None],
+            ["no-b", "leave", "operation_definition", None],
+            ["no-a", "leave", "document", None],
+            ["no-b", "leave", "document", None],
+        ]
+
+    @mark.parametrize("break_action", (BREAK, True), ids=("BREAK", "True"))
+    def allows_early_exit_while_visiting(break_action):
+        # Note: nearly identical to the above test but using ParallelVisitor.
+        ast = parse("{ a, b { x }, c }")
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+                if kind == "name" and node.value == "x":
+                    return break_action
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+
+        visit(ast, ParallelVisitor([TestVisitor()]))
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "b"],
+            ["leave", "name", "b"],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "x"],
+        ]
+
+    @mark.parametrize("break_action", (BREAK, True), ids=("BREAK", "True"))
+    def allows_early_exit_from_different_points(break_action):
+        ast = parse("{ a { y }, b { x } }")
+        visited = []
+
+        class TestVisitor(Visitor):
+            def __init__(self, name):
+                self.name = name
+
+            def enter(self, *args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                name = self.name
+                visited.append([f"break-{name}", "enter", kind, value])
+                if kind == "name" and node.value == name:
+                    return break_action
+
+            def leave(self, *args):
+                assert self.name == "b"
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                name = self.name
+                visited.append([f"break-{name}", "leave", kind, value])
+
+        visit(ast, ParallelVisitor([TestVisitor("a"), TestVisitor("b")]))
+        assert visited == [
+            ["break-a", "enter", "document", None],
+            ["break-b", "enter", "document", None],
+            ["break-a", "enter", "operation_definition", None],
+            ["break-b", "enter", "operation_definition", None],
+            ["break-a", "enter", "selection_set", None],
+            ["break-b", "enter", "selection_set", None],
+            ["break-a", "enter", "field", None],
+            ["break-b", "enter", "field", None],
+            ["break-a", "enter", "name", "a"],
+            ["break-b", "enter", "name", "a"],
+            ["break-b", "leave", "name", "a"],
+            ["break-b", "enter", "selection_set", None],
+            ["break-b", "enter", "field", None],
+            ["break-b", "enter", "name", "y"],
+            ["break-b", "leave", "name", "y"],
+            ["break-b", "leave", "field", None],
+            ["break-b", "leave", "selection_set", None],
+            ["break-b", "leave", "field", None],
+            ["break-b", "enter", "field", None],
+            ["break-b", "enter", "name", "b"],
+        ]
+
+    @mark.parametrize("break_action", (BREAK, True), ids=("BREAK", "True"))
+    def allows_early_exit_while_leaving(break_action):
+        # Note: nearly identical to the above test but using ParallelVisitor.
+        ast = parse("{ a, b { x }, c }")
+        visited = []
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+                if kind == "name" and node.value == "x":
+                    return break_action
+
+        visit(ast, ParallelVisitor([TestVisitor()]))
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "b"],
+            ["leave", "name", "b"],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "x"],
+            ["leave", "name", "x"],
+        ]
+
+    @mark.parametrize("break_action", (BREAK, True), ids=("BREAK", "True"))
+    def allows_early_exit_from_leaving_different_points(break_action):
+        ast = parse("{ a { y }, b { x } }")
+        visited = []
+
+        class TestVisitor(Visitor):
+            def __init__(self, name):
+                self.name = name
+
+            def enter(self, *args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                name = self.name
+                visited.append([f"break-{name}", "enter", kind, value])
+
+            def leave(self, *args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                name = self.name
+                visited.append([f"break-{name}", "leave", kind, value])
+                if kind == "field" and node.name.value == name:
+                    return break_action
+
+        visit(ast, ParallelVisitor([TestVisitor("a"), TestVisitor("b")]))
+        assert visited == [
+            ["break-a", "enter", "document", None],
+            ["break-b", "enter", "document", None],
+            ["break-a", "enter", "operation_definition", None],
+            ["break-b", "enter", "operation_definition", None],
+            ["break-a", "enter", "selection_set", None],
+            ["break-b", "enter", "selection_set", None],
+            ["break-a", "enter", "field", None],
+            ["break-b", "enter", "field", None],
+            ["break-a", "enter", "name", "a"],
+            ["break-b", "enter", "name", "a"],
+            ["break-a", "leave", "name", "a"],
+            ["break-b", "leave", "name", "a"],
+            ["break-a", "enter", "selection_set", None],
+            ["break-b", "enter", "selection_set", None],
+            ["break-a", "enter", "field", None],
+            ["break-b", "enter", "field", None],
+            ["break-a", "enter", "name", "y"],
+            ["break-b", "enter", "name", "y"],
+            ["break-a", "leave", "name", "y"],
+            ["break-b", "leave", "name", "y"],
+            ["break-a", "leave", "field", None],
+            ["break-b", "leave", "field", None],
+            ["break-a", "leave", "selection_set", None],
+            ["break-b", "leave", "selection_set", None],
+            ["break-a", "leave", "field", None],
+            ["break-b", "leave", "field", None],
+            ["break-b", "enter", "field", None],
+            ["break-b", "enter", "name", "b"],
+            ["break-b", "leave", "name", "b"],
+            ["break-b", "enter", "selection_set", None],
+            ["break-b", "enter", "field", None],
+            ["break-b", "enter", "name", "x"],
+            ["break-b", "leave", "name", "x"],
+            ["break-b", "leave", "field", None],
+            ["break-b", "leave", "selection_set", None],
+            ["break-b", "leave", "field", None],
+        ]
+
+    @mark.parametrize("remove_action", (REMOVE, Ellipsis), ids=("REMOVE", "Ellipsis"))
+    def allows_for_editing_on_enter(remove_action):
+        ast = parse("{ a, b, c { a, b, c } }", no_location=True)
+        visited = []
+
+        class TestVisitor1(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                if node.kind == "field" and node.name.value == "b":
+                    return remove_action
+
+        class TestVisitor2(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args_edited(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+
+        edited_ast = visit(ast, ParallelVisitor([TestVisitor1(), TestVisitor2()]))
+        assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
+        assert edited_ast == parse("{ a,    c { a,    c } }", no_location=True)
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "c"],
+            ["leave", "name", "c"],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "c"],
+            ["leave", "name", "c"],
+            ["leave", "field", None],
+            ["leave", "selection_set", None],
+            ["leave", "field", None],
+            ["leave", "selection_set", None],
+            ["leave", "operation_definition", None],
+            ["leave", "document", None],
+        ]
+
+    @mark.parametrize("remove_action", (REMOVE, Ellipsis), ids=("REMOVE", "Ellipsis"))
+    def allows_for_editing_on_leave(remove_action):
+        ast = parse("{ a, b, c { a, b, c } }", no_location=True)
+        visited = []
+
+        class TestVisitor1(Visitor):
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args_edited(ast, *args)
+                node = args[0]
+                if node.kind == "field" and node.name.value == "b":
+                    return remove_action
+
+        class TestVisitor2(Visitor):
+            @staticmethod
+            def enter(*args):
+                check_visitor_fn_args(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["enter", kind, value])
+
+            @staticmethod
+            def leave(*args):
+                check_visitor_fn_args_edited(ast, *args)
+                node = args[0]
+                kind, value = node.kind, get_value(node)
+                visited.append(["leave", kind, value])
+
+        edited_ast = visit(ast, ParallelVisitor([TestVisitor1(), TestVisitor2()]))
+        assert ast == parse("{ a, b, c { a, b, c } }", no_location=True)
+        assert edited_ast == parse("{ a,    c { a,    c } }", no_location=True)
+        assert visited == [
+            ["enter", "document", None],
+            ["enter", "operation_definition", None],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "b"],
+            ["leave", "name", "b"],
+            ["enter", "field", None],
+            ["enter", "name", "c"],
+            ["leave", "name", "c"],
+            ["enter", "selection_set", None],
+            ["enter", "field", None],
+            ["enter", "name", "a"],
+            ["leave", "name", "a"],
+            ["leave", "field", None],
+            ["enter", "field", None],
+            ["enter", "name", "b"],
+            ["leave", "name", "b"],
+            ["enter", "field", None],
+            ["enter", "name", "c"],
+            ["leave", "name", "c"],
+            ["leave", "field", None],
+            ["leave", "selection_set", None],
+            ["leave", "field", None],
+            ["leave", "selection_set", None],
+            ["leave", "operation_definition", None],
+            ["leave", "document", None],
+        ]
diff --git a/tests/pyutils/__init__.py b/tests/pyutils/__init__.py
new file mode 100644
index 0000000..c1675f5
--- /dev/null
+++ b/tests/pyutils/__init__.py
@@ -0,0 +1 @@
+"""Tests for graphql.pyutils"""
diff --git a/tests/pyutils/test_cached_property.py b/tests/pyutils/test_cached_property.py
new file mode 100644
index 0000000..1c8ad24
--- /dev/null
+++ b/tests/pyutils/test_cached_property.py
@@ -0,0 +1,27 @@
+from graphql.pyutils import cached_property
+
+
+def describe_cached_property():
+    def works_like_a_normal_property():
+        class TestClass:
+            @cached_property
+            def value(self):
+                return 42
+
+        assert TestClass().value == 42
+
+    def caches_the_value():
+        class TestClass:
+            evaluations = 0
+
+            @cached_property
+            def value(self):
+                self.__class__.evaluations += 1
+                return 42
+
+        obj = TestClass()
+        assert TestClass.evaluations == 0
+        assert obj.value == 42
+        assert TestClass.evaluations == 1
+        assert obj.value == 42
+        assert TestClass.evaluations == 1
diff --git a/tests/pyutils/test_convert_case.py b/tests/pyutils/test_convert_case.py
new file mode 100644
index 0000000..30a0d32
--- /dev/null
+++ b/tests/pyutils/test_convert_case.py
@@ -0,0 +1,51 @@
+from graphql.pyutils import camel_to_snake, snake_to_camel
+
+
+def describe_camel_to_snake():
+    def converts_typical_names():
+        result = camel_to_snake("CamelCase")
+        assert result == "camel_case"
+        result = camel_to_snake("InputObjectTypeExtensionNode")
+        assert result == "input_object_type_extension_node"
+
+    def may_start_with_lowercase():
+        result = camel_to_snake("CamelCase")
+        assert result == "camel_case"
+
+    def works_with_acronyms():
+        result = camel_to_snake("SlowXMLParser")
+        assert result == "slow_xml_parser"
+        result = camel_to_snake("FastGraphQLParser")
+        assert result == "fast_graph_ql_parser"
+
+    def keeps_already_snake():
+        result = camel_to_snake("snake_case")
+        assert result == "snake_case"
+
+
+def describe_snake_to_camel():
+    def converts_typical_names():
+        result = snake_to_camel("snake_case")
+        assert result == "SnakeCase"
+        result = snake_to_camel("input_object_type_extension_node")
+        assert result == "InputObjectTypeExtensionNode"
+
+    def may_start_with_uppercase():
+        result = snake_to_camel("Snake_case")
+        assert result == "SnakeCase"
+
+    def works_with_acronyms():
+        result = snake_to_camel("slow_xml_parser")
+        assert result == "SlowXmlParser"
+        result = snake_to_camel("fast_graph_ql_parser")
+        assert result == "FastGraphQlParser"
+
+    def keeps_already_camel():
+        result = snake_to_camel("CamelCase")
+        assert result == "CamelCase"
+
+    def can_produce_lower_camel_case():
+        result = snake_to_camel("snake_case", upper=False)
+        assert result == "snakeCase"
+        result = snake_to_camel("input_object_type_extension_node", False)
+        assert result == "inputObjectTypeExtensionNode"
diff --git a/tests/pyutils/test_description.py b/tests/pyutils/test_description.py
new file mode 100644
index 0000000..0a13822
--- /dev/null
+++ b/tests/pyutils/test_description.py
@@ -0,0 +1,233 @@
+from contextlib import contextmanager
+from typing import cast
+
+from pytest import raises  # type: ignore
+
+from graphql import graphql_sync
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLDirective,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLObjectType,
+    GraphQLNamedType,
+    GraphQLSchema,
+    GraphQLString,
+)
+from graphql.pyutils import (
+    Description,
+    is_description,
+    register_description,
+    unregister_description,
+)
+from graphql.utilities import get_introspection_query
+
+
+class LazyString:
+    def __init__(self, text: object):
+        self.text = text
+
+    def __str__(self) -> str:
+        return str(self.text)
+
+
+lazy_string = cast(str, LazyString("Why am I so lazy?"))
+
+
+@contextmanager
+def registered(base: type):
+    register_description(base)
+    try:
+        yield None
+    finally:
+        unregister_description(LazyString)
+
+
+def describe_description():
+    def by_default_strings_are_accepted():
+        is_description("")
+        is_description("text")
+
+    def by_default_non_strings_are_not_accepted():
+        assert not is_description(None)
+        assert not is_description(b"bytes")
+        assert not is_description(0)
+        assert not is_description(42)
+        assert not is_description(("tuple",))
+        assert not is_description(["list"])
+
+    def after_registration_lazy_strings_are_accepted():
+        with registered(LazyString):
+            assert is_description("not lazy")
+            assert is_description(lazy_string)
+            assert not is_description(42)
+
+    def can_register_and_unregister():
+        try:
+            assert Description.bases is str
+            register_description(str)
+            assert Description.bases is str
+            register_description(int)
+            assert Description.bases == (str, int)
+            register_description(int)
+            assert Description.bases == (str, int)
+            register_description(float)
+            assert Description.bases == (str, int, float)
+            unregister_description(int)
+            assert Description.bases == (str, float)
+            unregister_description(float)
+            assert Description.bases is str
+            unregister_description(str)
+            assert Description.bases is object
+            register_description(str)
+            assert Description.bases is str
+            register_description(object)
+            assert Description.bases is object
+            Description.bases = (str,)
+            unregister_description(str)
+            assert Description.bases is object
+            unregister_description(str)
+            assert Description.bases is object
+        finally:
+            Description.bases = str
+
+    def can_only_register_types():
+        with raises(TypeError, match="Only types can be registered\\."):
+            # noinspection PyTypeChecker
+            register_description("foo")  # type: ignore
+
+    def can_only_unregister_types():
+        with raises(TypeError, match="Only types can be unregistered\\."):
+            # noinspection PyTypeChecker
+            unregister_description("foo")  # type: ignore
+
+    def describe_graphql_types():
+        def graphql_named_type():
+            named_type = GraphQLNamedType(name="Foo", description="not lazy")
+            assert named_type.name == "Foo"
+            assert named_type.description == "not lazy"
+            with raises(TypeError, match="The name must be a string\\."):
+                GraphQLNamedType(name=lazy_string)
+            with raises(TypeError, match="The description must be a string\\."):
+                GraphQLNamedType(name="Foo", description=lazy_string)
+            with registered(LazyString):
+                named_type = GraphQLNamedType(name="Foo", description=lazy_string)
+                assert named_type.description is lazy_string
+                assert str(named_type.description).endswith("lazy?")
+                with raises(TypeError, match="The name must be a string\\."):
+                    GraphQLNamedType(name=lazy_string)
+
+        def graphql_field():
+            field = GraphQLField(GraphQLString, description="not lazy")
+            assert field.description == "not lazy"
+            field = GraphQLField(GraphQLString, deprecation_reason="not lazy")
+            assert field.deprecation_reason == "not lazy"
+            with raises(TypeError, match="The description must be a string\\."):
+                GraphQLField(GraphQLString, description=lazy_string)
+            with raises(TypeError, match="The deprecation reason must be a string\\."):
+                GraphQLField(GraphQLString, deprecation_reason=lazy_string)
+            with registered(LazyString):
+                field = GraphQLField(
+                    GraphQLString,
+                    description=lazy_string,
+                    deprecation_reason=lazy_string,
+                )
+                assert field.description is lazy_string
+                assert str(field.description).endswith("lazy?")
+                assert field.deprecation_reason is lazy_string
+                assert str(field.deprecation_reason).endswith("lazy?")
+
+        def graphql_argument():
+            arg = GraphQLArgument(GraphQLString, description="not lazy")
+            assert arg.description == "not lazy"
+            with raises(TypeError, match="Argument description must be a string\\."):
+                GraphQLArgument(GraphQLString, description=lazy_string)
+            with registered(LazyString):
+                arg = GraphQLArgument(GraphQLString, description=lazy_string)
+                assert arg.description is lazy_string
+                assert str(arg.description).endswith("lazy?")
+
+        def graphql_enum_value():
+            value = GraphQLEnumValue(description="not lazy")
+            assert value.description == "not lazy"
+            value = GraphQLEnumValue(deprecation_reason="not lazy")
+            assert value.deprecation_reason == "not lazy"
+            with raises(
+                TypeError, match="The description of the enum value must be a string\\."
+            ):
+                GraphQLEnumValue(description=lazy_string)
+            with raises(
+                TypeError,
+                match="The deprecation reason for the enum value must be a string\\.",
+            ):
+                GraphQLEnumValue(deprecation_reason=lazy_string)
+            with registered(LazyString):
+                value = GraphQLEnumValue(
+                    description=lazy_string, deprecation_reason=lazy_string
+                )
+                assert value.description is lazy_string
+                assert str(value.description).endswith("lazy?")
+                assert value.deprecation_reason is lazy_string
+                assert str(value.deprecation_reason).endswith("lazy?")
+
+        def graphql_input_field():
+            field = GraphQLInputField(GraphQLString, description="not lazy")
+            assert field.description == "not lazy"
+            with raises(TypeError, match="Input field description must be a string\\."):
+                GraphQLInputField(GraphQLString, description=lazy_string)
+            with registered(LazyString):
+                field = GraphQLInputField(GraphQLString, description=lazy_string)
+                assert field.description is lazy_string
+                assert str(field.description).endswith("lazy?")
+
+        def graphql_directive():
+            directive = GraphQLDirective("Foo", [], description="not lazy")
+            assert directive.name == "Foo"
+            assert directive.description == "not lazy"
+            with raises(TypeError, match="The directive name must be a string\\."):
+                GraphQLDirective(lazy_string, [])
+            with raises(TypeError, match="Foo description must be a string\\."):
+                GraphQLDirective("Foo", [], description=lazy_string)
+            with registered(LazyString):
+                directive = GraphQLDirective("Foo", [], description=lazy_string)
+                assert directive.description is lazy_string
+                assert str(directive.description).endswith("lazy?")
+                with raises(TypeError, match="The directive name must be a string\\."):
+                    GraphQLDirective(lazy_string, [])
+
+    def introspection():
+        class Lazy:
+            def __init__(self, text: str):
+                self.text = text
+                self.evaluated = False
+
+            def __str__(self) -> str:
+                self.evaluated = True
+                return self.text
+
+        description = Lazy("a lazy description")
+        deprecation_reason = Lazy("a lazy reason")
+
+        with registered(Lazy):
+            field = GraphQLField(
+                GraphQLString,
+                description=cast(str, description),
+                deprecation_reason=cast(str, deprecation_reason),
+            )
+
+        schema = GraphQLSchema(GraphQLObjectType("Query", {"lazyField": field}))
+
+        query = get_introspection_query(descriptions=True)
+        assert not description.evaluated
+        assert not deprecation_reason.evaluated
+        result = graphql_sync(schema, query)
+        assert description.evaluated
+        assert deprecation_reason.evaluated
+        assert result.data
+        introspected_query = result.data["__schema"]["types"][0]
+        assert introspected_query["name"] == "Query"
+        introspected_field = introspected_query["fields"][0]
+        assert introspected_field["name"] == "lazyField"
+        assert introspected_field["description"] == "a lazy description"
+        assert introspected_field["deprecationReason"] == "a lazy reason"
diff --git a/tests/pyutils/test_did_you_mean.py b/tests/pyutils/test_did_you_mean.py
new file mode 100644
index 0000000..36ac493
--- /dev/null
+++ b/tests/pyutils/test_did_you_mean.py
@@ -0,0 +1,24 @@
+from graphql.pyutils import did_you_mean
+
+
+def describe_did_you_mean():
+    def does_accept_an_empty_list():
+        assert did_you_mean([]) == ""
+
+    def handles_single_suggestion():
+        assert did_you_mean(["A"]) == " Did you mean 'A'?"
+
+    def handles_two_suggestions():
+        assert did_you_mean(["A", "B"]) == " Did you mean 'A' or 'B'?"
+
+    def handles_multiple_suggestions():
+        assert did_you_mean(["A", "B", "C"]) == " Did you mean 'A', 'B', or 'C'?"
+
+    def limits_to_five_suggestions():
+        assert (
+            did_you_mean(["A", "B", "C", "D", "E", "F"])
+            == " Did you mean 'A', 'B', 'C', 'D', or 'E'?"
+        )
+
+    def adds_sub_message():
+        assert did_you_mean(["A"], "the letter") == " Did you mean the letter 'A'?"
diff --git a/tests/pyutils/test_event_emitter.py b/tests/pyutils/test_event_emitter.py
new file mode 100644
index 0000000..28f5076
--- /dev/null
+++ b/tests/pyutils/test_event_emitter.py
@@ -0,0 +1,122 @@
+from asyncio import sleep
+
+from pytest import mark, raises  # type: ignore
+
+from graphql.pyutils import EventEmitter, EventEmitterAsyncIterator
+
+
+def describe_event_emitter():
+    def add_and_remove_listeners():
+        emitter = EventEmitter()
+
+        def listener1(_value):
+            pass
+
+        def listener2(_value):
+            pass
+
+        emitter.add_listener("foo", listener1)
+        emitter.add_listener("foo", listener2)
+        emitter.add_listener("bar", listener1)
+        assert emitter.listeners["foo"] == [listener1, listener2]
+        assert emitter.listeners["bar"] == [listener1]
+        emitter.remove_listener("foo", listener1)
+        assert emitter.listeners["foo"] == [listener2]
+        assert emitter.listeners["bar"] == [listener1]
+        emitter.remove_listener("foo", listener2)
+        assert emitter.listeners["foo"] == []
+        assert emitter.listeners["bar"] == [listener1]
+        emitter.remove_listener("bar", listener1)
+        assert emitter.listeners["bar"] == []
+
+    def emit_sync():
+        emitter = EventEmitter()
+        emitted = []
+
+        def listener(value):
+            emitted.append(value)
+
+        emitter.add_listener("foo", listener)
+        assert emitter.emit("foo", "bar") is True
+        assert emitted == ["bar"]
+        assert emitter.emit("bar", "baz") is False
+        assert emitted == ["bar"]
+
+    @mark.asyncio
+    async def emit_async():
+        emitter = EventEmitter()
+        emitted = []
+
+        async def listener(value):
+            emitted.append(value)
+
+        emitter.add_listener("foo", listener)
+        emitter.emit("foo", "bar")
+        emitter.emit("bar", "baz")
+        await sleep(0)
+        assert emitted == ["bar"]
+
+
+def describe_event_emitter_async_iterator():
+    @mark.asyncio
+    async def subscribe_async_iterator_mock():
+        # Create an AsyncIterator from an EventEmitter
+        emitter = EventEmitter()
+        iterator = EventEmitterAsyncIterator(emitter, "publish")
+
+        # Make sure it works as an async iterator
+        assert iterator.__aiter__() is iterator
+        assert callable(iterator.__anext__)
+
+        # Queue up publishes
+        assert emitter.emit("publish", "Apple") is True
+        assert emitter.emit("publish", "Banana") is True
+
+        # Read payloads
+        assert await iterator.__anext__() == "Apple"
+        assert await iterator.__anext__() == "Banana"
+
+        # Read ahead
+        i3 = iterator.__anext__()
+        i4 = iterator.__anext__()
+
+        # Publish
+        assert emitter.emit("publish", "Coconut") is True
+        assert emitter.emit("publish", "Durian") is True
+
+        # Await results
+        assert await i3 == "Coconut"
+        assert await i4 == "Durian"
+
+        # Read ahead
+        i5 = iterator.__anext__()
+
+        # Terminate emitter
+        await iterator.aclose()
+
+        # Publish is not caught after terminate
+        assert emitter.emit("publish", "Fig") is False
+
+        # Find that cancelled read-ahead got a "done" result
+        with raises(StopAsyncIteration):
+            await i5
+
+        # And next returns empty completion value
+        with raises(StopAsyncIteration):
+            await iterator.__anext__()
+
+    @mark.asyncio
+    async def aclose_cleans_up():
+        emitter = EventEmitter()
+        assert emitter.listeners["publish"] == []
+        iterator = EventEmitterAsyncIterator(emitter, "publish")
+        assert emitter.listeners["publish"] == [iterator.queue.put]
+        assert not iterator.closed
+        for value in range(3):
+            emitter.emit("publish", value)
+        await sleep(0)
+        assert iterator.queue.qsize() == 3
+        await iterator.aclose()
+        assert emitter.listeners["publish"] == []
+        assert iterator.queue.empty()
+        assert iterator.closed
diff --git a/tests/pyutils/test_frozen_dict.py b/tests/pyutils/test_frozen_dict.py
new file mode 100644
index 0000000..9e4eace
--- /dev/null
+++ b/tests/pyutils/test_frozen_dict.py
@@ -0,0 +1,97 @@
+from copy import copy, deepcopy
+
+from pytest import raises  # type: ignore
+
+from graphql.pyutils import FrozenError, FrozenDict
+
+
+def describe_frozen_list():
+    def can_read():
+        fd = FrozenDict({1: 2, 3: 4})
+        assert fd == {1: 2, 3: 4}
+        assert list(i for i in fd) == [1, 3]
+        assert fd.copy() == fd
+        assert 3 in fd
+        assert 2 not in fd
+        assert fd[1] == 2
+        with raises(KeyError):
+            # noinspection PyStatementEffect
+            fd[2]
+        assert len(fd) == 2
+        assert fd.get(1) == 2
+        assert fd.get(2, 5) == 5
+        assert list(fd.items()) == [(1, 2), (3, 4)]
+        assert list(fd.keys()) == [1, 3]
+        assert list(fd.values()) == [2, 4]
+
+    def cannot_write():
+        fd = FrozenDict({1: 2, 3: 4})
+        with raises(FrozenError):
+            fd[1] = 2
+        with raises(FrozenError):
+            fd[4] = 5
+        with raises(FrozenError):
+            del fd[1]
+        with raises(FrozenError):
+            del fd[3]
+        with raises(FrozenError):
+            fd.clear()
+        with raises(FrozenError):
+            fd.pop(1)
+        with raises(FrozenError):
+            fd.pop(4, 5)
+        with raises(FrozenError):
+            fd.popitem()
+        with raises(FrozenError):
+            fd.setdefault(1, 2)
+        with raises(FrozenError):
+            fd.setdefault(4, 5)
+        with raises(FrozenError):
+            fd.update({1: 2})
+        with raises(FrozenError):
+            fd.update({4: 5})
+        with raises(FrozenError):
+            fd += {4: 5}
+        assert fd == {1: 2, 3: 4}
+
+    def can_hash():
+        fd1 = FrozenDict({1: 2, 3: 4})
+        fd2 = FrozenDict({1: 2, 3: 4})
+        assert fd2 == fd1
+        assert fd2 is not fd1
+        assert hash(fd2) is not hash(fd1)
+        fd3 = FrozenDict({1: 2, 3: 5})
+        assert fd3 != fd1
+        assert hash(fd3) != hash(fd1)
+
+    def can_copy():
+        fd1 = FrozenDict({1: 2, 3: 4})
+        fd2 = fd1.copy()
+        assert isinstance(fd2, FrozenDict)
+        assert fd2 == fd1
+        assert hash(fd2) == hash(fd1)
+        assert fd2 is not fd1
+        fd3 = copy(fd1)
+        assert isinstance(fd3, FrozenDict)
+        assert fd3 == fd1
+        assert hash(fd3) == hash(fd1)
+        assert fd3 is not fd1
+
+    def can_deep_copy():
+        fd11 = FrozenDict({1: 2, 3: 4})
+        fd12 = FrozenDict({2: 1, 4: 3})
+        fd1 = FrozenDict({1: fd11, 2: fd12})
+        assert fd1[1] is fd11
+        assert fd1[2] is fd12
+        fd2 = deepcopy(fd1)
+        assert isinstance(fd2, FrozenDict)
+        assert fd2 == fd1
+        assert hash(fd2) == hash(fd1)
+        fd21 = fd2[1]
+        fd22 = fd2[2]
+        assert isinstance(fd21, FrozenDict)
+        assert isinstance(fd22, FrozenDict)
+        assert fd21 == fd11
+        assert fd21 is not fd11
+        assert fd22 == fd12
+        assert fd22 is not fd12
diff --git a/tests/pyutils/test_frozen_error.py b/tests/pyutils/test_frozen_error.py
new file mode 100644
index 0000000..990b570
--- /dev/null
+++ b/tests/pyutils/test_frozen_error.py
@@ -0,0 +1,6 @@
+from graphql.pyutils import FrozenError
+
+
+def describe_frozen_error():
+    def frozen_error_is_type_error():
+        assert issubclass(FrozenError, TypeError)
diff --git a/tests/pyutils/test_frozen_list.py b/tests/pyutils/test_frozen_list.py
new file mode 100644
index 0000000..a7ad69f
--- /dev/null
+++ b/tests/pyutils/test_frozen_list.py
@@ -0,0 +1,113 @@
+from copy import copy, deepcopy
+
+from pytest import raises  # type: ignore
+
+from graphql.pyutils import FrozenError, FrozenList
+
+
+def describe_frozen_list():
+    def can_read():
+        fl = FrozenList([1, 2, 3])
+        assert fl == [1, 2, 3]
+        assert list(i for i in fl) == fl
+        assert fl.copy() == fl
+        assert 2 in fl
+        assert 4 not in fl
+        assert fl + [4, 5] == [1, 2, 3, 4, 5]
+        assert [4, 5] + fl == [4, 5, 1, 2, 3]
+        assert fl * 2 == [1, 2, 3, 1, 2, 3]
+        assert 2 * fl == [1, 2, 3, 1, 2, 3]
+        assert fl[1] == 2
+        with raises(IndexError):
+            fl[3]
+        assert fl[1:4] == [2, 3]
+        assert fl[::2] == [1, 3]
+        assert len(fl) == 3
+        assert min(fl) == 1
+        assert max(fl) == 3
+        assert sum(fl) == 6
+        assert fl.index(2) == 1
+        with raises(ValueError):
+            fl.index(4)
+        assert fl.count(2) == 1
+        assert fl.count(4) == 0
+        assert list(reversed(fl)) == [3, 2, 1]
+        assert sorted(fl) == [1, 2, 3]
+
+    def cannot_write():
+        fl = FrozenList([1, 2, 3])
+        with raises(FrozenError):
+            fl[1] = 4
+        with raises(FrozenError):
+            fl[1:4] = [4]
+        with raises(FrozenError):
+            del fl[1]
+        with raises(FrozenError):
+            del fl[1:4]
+        with raises(FrozenError):
+            fl[1::2] = [4]
+        with raises(FrozenError):
+            del fl[::2]
+        with raises(FrozenError):
+            fl.append(4)
+        with raises(FrozenError):
+            fl.clear()
+        with raises(FrozenError):
+            fl.extend([4])
+        with raises(FrozenError):
+            fl += [4]
+        with raises(FrozenError):
+            fl *= 2
+        with raises(FrozenError):
+            fl.insert(1, 4)
+        with raises(FrozenError):
+            fl.pop()
+        with raises(FrozenError):
+            fl.remove(2)
+        with raises(FrozenError):
+            fl.sort()
+        with raises(FrozenError):
+            fl.reverse()
+        assert fl == [1, 2, 3]
+
+    def can_add_rol():
+        fl1 = FrozenList([1, 2])
+        rol2 = FrozenList([3, 4])
+        assert fl1 + rol2 == [1, 2, 3, 4]
+
+    def can_add_tuple():
+        fl = FrozenList([1, 2])
+        assert fl + (3, 4) == [1, 2, 3, 4]
+
+    def can_hash():
+        fl1 = FrozenList([1, 2])
+        fl2 = FrozenList([1, 2])
+        assert fl2 == fl1
+        assert fl2 is not fl1
+        assert hash(fl2) == hash(fl1)
+        fl3 = FrozenList([1, 3])
+        assert fl3 != fl1
+        assert hash(fl3) != hash(fl1)
+
+    def can_copy():
+        fl1 = FrozenList([1, 2])
+        fl2 = copy(fl1)
+        assert isinstance(fl2, FrozenList)
+        assert fl2 == fl1
+        assert hash(fl2) == hash(fl1)
+        assert fl2 is not fl1
+
+    def can_deep_copy():
+        fl11 = FrozenList([1, 2])
+        fl12 = FrozenList([2, 1])
+        fl1 = FrozenList([fl11, fl12])
+        fl2 = deepcopy(fl1)
+        assert isinstance(fl2, FrozenList)
+        assert fl2 == fl1
+        assert hash(fl2) == hash(fl1)
+        assert isinstance(fl2[0], FrozenList)
+        assert isinstance(fl2[1], FrozenList)
+        assert fl2[0] == fl1[0]
+        assert fl2[0] is not fl1[0]
+        assert fl2[1] == fl1[1]
+        assert fl2[1] is not fl1[1]
diff --git a/tests/pyutils/test_identity_func.py b/tests/pyutils/test_identity_func.py
new file mode 100644
index 0000000..b3ca514
--- /dev/null
+++ b/tests/pyutils/test_identity_func.py
@@ -0,0 +1,17 @@
+from graphql.pyutils import identity_func, Undefined
+
+
+def describe_identity_func():
+    def returns_the_first_argument_it_receives():
+        assert identity_func() is Undefined
+        assert identity_func(Undefined) is Undefined
+        assert identity_func(None) is None
+        obj = object()
+        assert identity_func(obj) is obj
+
+        assert identity_func(Undefined, None) is Undefined
+        assert identity_func(None, Undefined) is None
+
+        assert identity_func(None, Undefined, obj) is None
+        assert identity_func(Undefined, None, obj) is Undefined
+        assert identity_func(obj, None, Undefined) is obj
diff --git a/tests/pyutils/test_inspect.py b/tests/pyutils/test_inspect.py
new file mode 100644
index 0000000..163b04d
--- /dev/null
+++ b/tests/pyutils/test_inspect.py
@@ -0,0 +1,379 @@
+from math import nan, inf
+from contextlib import contextmanager
+from importlib import import_module
+from typing import Any, Dict, FrozenSet, List, Set, Tuple
+
+from pytest import mark  # type: ignore
+
+from graphql.pyutils import inspect, Undefined
+from graphql.type import (
+    GraphQLDirective,
+    GraphQLField,
+    GraphQLInt,
+    GraphQLList,
+    GraphQLObjectType,
+    GraphQLNonNull,
+    GraphQLString,
+)
+
+inspect_module = import_module(inspect.__module__)
+
+
+@contextmanager
+def increased_recursive_depth():
+    inspect_module.max_recursive_depth += 1  # type: ignore
+    try:
+        yield inspect
+    finally:
+        inspect_module.max_recursive_depth -= 1  # type: ignore
+
+
+@contextmanager
+def increased_str_size():
+    inspect_module.max_str_size *= 2  # type: ignore
+    try:
+        yield inspect
+    finally:
+        inspect_module.max_str_size //= 2  # type: ignore
+
+
+@contextmanager
+def increased_list_size():
+    inspect_module.max_list_size *= 2  # type: ignore
+    try:
+        yield inspect
+    finally:
+        inspect_module.max_list_size //= 2  # type: ignore
+
+
+def describe_inspect():
+    def inspect_invalid():
+        assert inspect(Undefined) == "Undefined"
+
+    def inspect_none():
+        assert inspect(None) == "None"
+
+    def inspect_boolean():
+        assert inspect(True) == "True"
+        assert inspect(False) == "False"
+
+    def inspect_string():
+        for s in "", "abc", "foo\tbar \u265e\0", "'", "'":
+            assert inspect(s) == repr(s)
+
+    def overly_large_string():
+        s = "foo" * 100
+        r = repr(s)
+        assert inspect(s) == r[:118] + "..." + r[-119:]
+        with increased_str_size():
+            assert inspect(s) == r
+
+    def inspect_bytes():
+        for b in b"", b"abc", b"foo\tbar \x7f\xff\0", b"'", b"'":
+            assert inspect(b) == repr(b)
+            a = bytearray(b)
+            assert inspect(a) == repr(a)
+
+    def overly_many_bytes():
+        b = b"foo" * 100
+        r = repr(b)
+        assert inspect(b) == r[:118] + "..." + r[-119:]
+        a = bytearray(b)
+        r = repr(a)
+        assert inspect(a) == r[:118] + "..." + r[-119:]
+
+    def inspect_numbers():
+        assert inspect(0) == "0"
+        assert inspect(0.0) == "0.0"
+        assert inspect(314) == "314"
+        assert inspect(3.14) == "3.14"
+        assert inspect(complex(1, 2)) == "(1+2j)"
+        assert inspect(nan) == "nan"
+        assert inspect(inf) == "inf"
+        assert inspect(-inf) == "-inf"
+
+    def overly_large_int():
+        n = int("123" * 100)
+        r = repr(n)
+        assert inspect(n) == r[:118] + "..." + r[-119:]
+        with increased_str_size():
+            assert inspect(n) == r
+
+    def inspect_function():
+        assert inspect(lambda: 0) == "<function>"  # pragma: no cover
+
+        def test_func():
+            pass
+
+        assert inspect(test_func) == "<function test_func>"
+
+    def inspect_exception():
+        assert inspect(ValueError) == "<exception class ValueError>"
+        assert inspect(ArithmeticError(42)) == "<exception ArithmeticError>"
+
+    def inspect_class_and_method():
+        class TestClass:
+            def test_method(self):
+                pass
+
+        assert inspect(TestClass) == "<class TestClass>"
+        assert inspect(TestClass()) == "<TestClass instance>"
+        assert inspect(TestClass.test_method) == "<function test_method>"
+        assert inspect(TestClass().test_method) == "<method test_method>"
+
+    def inspect_unknown_object():
+        class MetaClass(type):
+            __name__ = ""
+
+        class TestClass(metaclass=MetaClass):
+            pass
+
+        assert inspect(TestClass()) == "<object>"
+
+    def inspect_generator():
+        def test_generator():
+            yield None  # pragma: no cover
+
+        assert inspect(test_generator) == "<generator function test_generator>"
+        assert inspect(test_generator()) == "<generator test_generator>"
+
+    @mark.asyncio
+    async def inspect_coroutine():
+        async def test_coroutine():
+            pass
+
+        assert inspect(test_coroutine) == "<coroutine function test_coroutine>"
+        coroutine_object = test_coroutine()
+        assert inspect(coroutine_object) == "<coroutine test_coroutine>"
+        await coroutine_object  # avoid warning
+
+    def inspect_async_generator():
+        async def test_async_generator():
+            yield None  # pragma: no cover
+
+        assert inspect(test_async_generator) == (
+            "<async generator function test_async_generator>"
+        )
+        assert inspect(test_async_generator()) == (
+            "<async generator test_async_generator>"
+        )
+
+    def inspect_lists():
+        assert inspect([]) == "[]"
+        assert inspect([None]) == "[None]"
+        assert inspect([[None]]) == "[[None]]"
+        assert inspect([1, nan]) == "[1, nan]"
+        assert inspect([["a", "b"], "c"]) == "[['a', 'b'], 'c']"
+
+    def inspect_overly_large_list():
+        s: List[int] = list(range(20))
+        assert inspect(s) == "[0, 1, 2, 3, 4, ..., 16, 17, 18, 19]"
+        with increased_list_size():
+            assert inspect(s) == repr(s)
+
+    def inspect_overly_nested_list():
+        s: List[List[List]] = [[[]]]
+        assert inspect(s) == "[[[]]]"
+        s = [[[1, 2, 3]]]
+        assert inspect(s) == "[[[...]]]"
+        with increased_recursive_depth():
+            assert inspect(s) == repr(s)
+
+    def inspect_recursive_list():
+        s: List[Any] = [1, 2, 3]
+        s[1] = s
+        assert inspect(s) == "[1, [...], 3]"
+
+    def inspect_tuples():
+        assert inspect(()) == "()"
+        assert inspect((None,)) == "(None,)"
+        assert inspect(((None,),)) == "((None,),)"
+        assert inspect((1, nan)) == "(1, nan)"
+        assert inspect((("a", "b"), "c")) == "(('a', 'b'), 'c')"
+
+    def inspect_overly_large_tuple():
+        s = tuple(range(20))
+        assert inspect(s) == "(0, 1, 2, 3, 4, ..., 16, 17, 18, 19)"
+        with increased_list_size():
+            assert inspect(s) == repr(s)
+
+    def inspect_overly_nested_tuple():
+        s: Tuple[Tuple[Tuple]] = (((),),)
+        assert inspect(s) == "(((),),)"
+        s = (((1, 2, 3),),)
+        assert inspect(s) == "(((...),),)"
+        with increased_recursive_depth():
+            assert inspect(s) == repr(s)
+
+    def inspect_recursive_tuple():
+        s: List[Any] = [1, 2, 3]
+        s[1] = s
+        t = tuple(s)
+        assert inspect(t) == "(1, [1, [...], 3], 3)"
+
+    def mixed_lists_and_tuples():
+        assert inspect(["a", ("b",)]) == "['a', ('b',)]"
+
+    def mixed_lists_and_tuples_with_various_objects():
+        class TestClass:
+            pass
+
+        assert inspect([TestClass, (TestClass,), ValueError()]) == (
+            "[<class TestClass>, (<class TestClass>,), <exception ValueError>]"
+        )
+
+    def inspect_dicts():
+        assert inspect({}) == "{}"
+        assert inspect({"a": 1}) == "{'a': 1}"
+        assert inspect({"a": 1, "b": 2}) == "{'a': 1, 'b': 2}"
+        assert inspect({"list": [None, 0]}) == "{'list': [None, 0]}"
+        assert inspect({"a": True, "b": None}) == "{'a': True, 'b': None}"
+
+    def inspect_overly_large_dict():
+        s = dict(zip((chr(97 + i) for i in range(20)), range(20)))
+        assert (
+            inspect(s) == "{'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4,"
+            " ..., 'q': 16, 'r': 17, 's': 18, 't': 19}"
+        )
+        with increased_list_size():
+            assert inspect(s) == repr(s)
+
+    def inspect_overly_nested_dict():
+        s: Dict[str, Dict[str, Dict]] = {"a": {"b": {}}}
+        assert inspect(s) == "{'a': {'b': {}}}"
+        s = {"a": {"b": {"c": 3}}}
+        assert inspect(s) == "{'a': {'b': {...}}}"
+        with increased_recursive_depth():
+            assert inspect(s) == repr(s)
+
+    def inspect_recursive_dict():
+        s: Dict[int, Any] = {}
+        s[1] = s
+        assert inspect(s) == "{1: {...}}"
+
+    def inspect_sets():
+        assert inspect(set()) == "set()"
+        assert inspect({"a"}) == "{'a'}"
+        assert inspect({"a", 1}) in ("{'a', 1}", "{1, 'a'}")  # sets are unordered
+
+    def inspect_overly_large_set():
+        s = set(range(20))
+        r = inspect(s)
+        assert r.startswith("{") and r.endswith("}")
+        assert "..., " in r and "5" not in s  # sets are unordered
+        assert len(r) == 36
+        with increased_list_size():
+            assert inspect(s) == repr(s)
+
+    def inspect_overly_nested_set():
+        s: List[List[Set]] = [[set()]]
+        assert inspect(s) == "[[set()]]"
+        s = [[{1, 2, 3}]]
+        assert inspect(s) == "[[set(...)]]"
+        with increased_recursive_depth():
+            assert inspect(s) == repr(s)
+
+    def inspect_frozensets():
+        assert inspect(frozenset()) == "frozenset()"
+        assert inspect(frozenset(["a"])) == "frozenset({'a'})"
+        assert inspect(frozenset(["a", 1])) in (
+            "frozenset({'a', 1})",
+            "frozenset({1, 'a'})",
+        )  # frozensets are unordered
+
+    def inspect_overly_large_frozenset():
+        s = frozenset(range(20))
+        r = inspect(s)
+        assert r.startswith("frozenset({") and r.endswith("})")
+        assert "..., " in r and "5" not in s  # frozensets are unordered
+        assert len(r) == 47
+        with increased_list_size():
+            assert inspect(s) == repr(s)
+
+    def inspect_overly_nested_frozenset():
+        s: FrozenSet[FrozenSet[FrozenSet]] = frozenset([frozenset([frozenset()])])
+        assert inspect(s) == "frozenset({frozenset({frozenset()})})"
+        s = frozenset([frozenset([frozenset([1, 2, 3])])])
+        assert inspect(s) == "frozenset({frozenset({frozenset(...)})})"
+        with increased_recursive_depth():
+            assert inspect(s) == repr(s)
+
+    def mixed_recursive_dict_and_list():
+        s: Any = {1: []}
+        s[1].append(s)
+        assert inspect(s) == "{1: [{...}]}"
+        s = [1, 2, 3]
+        s[1] = {2: s}
+        assert inspect(s) == "[1, {2: [...]}, 3]"
+
+    def mixed_dicts_and_sets():
+        assert inspect({"a": {"b"}}) == "{'a': {'b'}}"
+        assert inspect({1: [], 2: (), 3: set()}) == "{1: [], 2: (), 3: set()}"
+        assert inspect([(set(),), {None: {()}}]) == "[(set(),), {None: set(...)}]"
+
+    def mixed_dicts_and_sets_with_various_objects():
+        class TestClass:
+            pass
+
+        assert inspect({TestClass: {ValueError()}, ValueError: {TestClass()}}) == (
+            "{<class TestClass>: {<exception ValueError>},"
+            " <exception class ValueError>: {<TestClass instance>}}"
+        )
+
+    def inspect_graphql_types():
+        assert inspect(GraphQLInt) == "Int"
+        assert inspect(GraphQLString) == "String"
+        assert inspect(GraphQLNonNull(GraphQLString)) == "String!"
+        assert inspect(GraphQLList(GraphQLString)) == "[String]"
+        test_object_type = GraphQLObjectType(
+            "TestObjectType", {"test": GraphQLField(GraphQLString)}
+        )
+        assert inspect(test_object_type) == "TestObjectType"
+        test_directive = GraphQLDirective("TestDirective", [])
+        assert inspect(test_directive) == "@TestDirective"
+
+    def custom_inspect():
+        class TestClass:
+            @staticmethod
+            def __inspect__():
+                return "<custom magic method inspect>"
+
+        assert inspect(TestClass()) == "<custom magic method inspect>"
+
+    def custom_inspect_that_uses_self():
+        class TestClass:
+            str = "Hello World!"
+
+            def __inspect__(self):
+                return self.str
+
+        assert inspect(TestClass()) == "Hello World!"
+
+    def custom_inspect_that_returns_a_list():
+        class TestClass:
+            @staticmethod
+            def __inspect__():
+                return [1, 2, 3]
+
+        assert inspect(TestClass()) == "[1, 2, 3]"
+
+    def custom_inspect_that_returns_an_overly_large_string():
+        s = "foo" * 100
+
+        class TestClass:
+            @staticmethod
+            def __inspect__():
+                return s
+
+        value = TestClass()
+
+        assert inspect(value) == s[:118] + "..." + s[-119:]
+        with increased_str_size():
+            assert inspect(value) == s
+
+    def custom_inspect_that_is_recursive():
+        class TestClass:
+            def __inspect__(self):
+                return self
+
+        assert inspect(TestClass()) == "<TestClass instance>"
diff --git a/tests/pyutils/test_is_awaitable.py b/tests/pyutils/test_is_awaitable.py
new file mode 100644
index 0000000..c322177
--- /dev/null
+++ b/tests/pyutils/test_is_awaitable.py
@@ -0,0 +1,105 @@
+import asyncio
+from inspect import isawaitable
+
+from pytest import mark  # type: ignore
+
+from graphql.pyutils import is_awaitable
+
+
+def describe_is_awaitable():
+    def declines_the_none_value():
+        assert not isawaitable(None)
+        assert not is_awaitable(None)
+
+    def declines_a_boolean_value():
+        assert not isawaitable(True)
+        assert not is_awaitable(True)
+
+    def declines_an_int_value():
+        assert not is_awaitable(42)
+
+    def declines_a_string_value():
+        assert not isawaitable("some_string")
+        assert not is_awaitable("some_string")
+
+    def declines_a_dict_value():
+        assert not isawaitable({})
+        assert not is_awaitable({})
+
+    def declines_an_object_instance():
+        assert not isawaitable(object())
+        assert not is_awaitable(object())
+
+    def declines_the_type_class():
+        assert not isawaitable(type)
+        assert not is_awaitable(type)
+
+    def declines_a_lambda_function():
+        assert not isawaitable(lambda: True)  # pragma: no cover
+        assert not is_awaitable(lambda: True)  # pragma: no cover
+
+    def declines_a_normal_function():
+        def some_function():
+            return True
+
+        assert not isawaitable(some_function())
+        assert not is_awaitable(some_function)
+
+    def declines_a_normal_generator_function():
+        def some_generator():
+            yield True  # pragma: no cover
+
+        assert not isawaitable(some_generator)
+        assert not is_awaitable(some_generator)
+
+    def declines_a_normal_generator_object():
+        def some_generator():
+            yield True  # pragma: no cover
+
+        assert not isawaitable(some_generator())
+        assert not is_awaitable(some_generator())
+
+    def declines_a_coroutine_function():
+        async def some_coroutine():
+            return True  # pragma: no cover
+
+        assert not isawaitable(some_coroutine)
+        assert not is_awaitable(some_coroutine)
+
+    @mark.asyncio
+    @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+    async def recognizes_a_coroutine_object():
+        async def some_coroutine():
+            return False  # pragma: no cover
+
+        assert isawaitable(some_coroutine())
+        assert is_awaitable(some_coroutine())
+
+    @mark.filterwarnings("ignore::Warning")  # Deprecation and Runtime
+    def recognizes_an_old_style_coroutine():
+        @asyncio.coroutine
+        def some_old_style_coroutine():
+            yield False  # pragma: no cover
+
+        assert is_awaitable(some_old_style_coroutine())
+        assert is_awaitable(some_old_style_coroutine())
+
+    @mark.asyncio
+    @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+    async def recognizes_a_future_object():
+        async def some_coroutine():
+            return False  # pragma: no cover
+
+        some_future = asyncio.ensure_future(some_coroutine())
+
+        assert is_awaitable(some_future)
+        assert is_awaitable(some_future)
+
+    @mark.asyncio
+    @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+    def declines_an_async_generator():
+        async def some_async_generator():
+            yield True  # pragma: no cover
+
+        assert not isawaitable(some_async_generator())
+        assert not is_awaitable(some_async_generator())
diff --git a/tests/pyutils/test_is_collection.py b/tests/pyutils/test_is_collection.py
new file mode 100644
index 0000000..c043418
--- /dev/null
+++ b/tests/pyutils/test_is_collection.py
@@ -0,0 +1,58 @@
+from collections import defaultdict, namedtuple
+from itertools import count
+
+from graphql.pyutils import FrozenDict, FrozenList, is_collection
+
+
+def describe_is_collection():
+    def null_is_not_a_collection():
+        assert is_collection(None) is False
+
+    def a_string_is_not_a_collection():
+        assert is_collection("") is False
+        assert is_collection("text") is False
+
+    def a_byte_string_is_not_a_collection():
+        assert is_collection(b"") is False
+        assert is_collection(b"bytes") is False
+
+    def a_list_is_a_collection():
+        assert is_collection([]) is True
+        assert is_collection([1, 2, 3]) is True
+
+    def a_tuple_is_a_collection():
+        assert is_collection(()) is True
+        assert is_collection((1, 2, 3)) is True
+
+    def a_namedtuple_is_a_collection():
+        named = namedtuple("named", "a b c")
+        assert is_collection(named(1, 2, 3)) is True
+
+    def a_dict_is_not_a_collection():
+        assert is_collection({}) is False
+        assert is_collection({1: 2, 3: 4}) is False
+
+    def a_defaultdict_is_not_a_collection():
+        assert is_collection(defaultdict(list)) is False
+
+    def a_keys_view_is_a_collection():
+        assert is_collection({}.keys()) is True
+        assert is_collection({1: 2, 3: 4}.keys()) is True
+
+    def a_values_view_is_a_collection():
+        assert is_collection({}.values()) is True
+        assert is_collection({1: 2, 3: 4}.values()) is True
+
+    def a_range_is_a_collection():
+        assert is_collection(range(10)) is True
+
+    def an_infinite_generator_is_not_a_collection():
+        assert is_collection(count()) is False
+
+    def a_frozen_list_is_a_collection():
+        assert is_collection(FrozenList()) is True
+        assert is_collection(FrozenList([1, 2, 3])) is True
+
+    def a_frozen_dict_is_not_a_collection():
+        assert is_collection(FrozenDict()) is False
+        assert is_collection(FrozenDict({1: 2, 3: 4})) is False
diff --git a/tests/pyutils/test_is_finite.py b/tests/pyutils/test_is_finite.py
new file mode 100644
index 0000000..faf42cb
--- /dev/null
+++ b/tests/pyutils/test_is_finite.py
@@ -0,0 +1,40 @@
+from math import inf, nan
+
+from graphql.pyutils import is_finite, Undefined
+
+
+def describe_is_finite():
+    def null_is_not_finite():
+        assert is_finite(None) is False
+
+    def booleans_are_not_finite():
+        # they should not be considered as integers 0 and 1
+        assert is_finite(False) is False
+        assert is_finite(True) is False
+
+    def strings_are_not_finite():
+        assert is_finite("string") is False
+
+    def ints_are_finite():
+        assert is_finite(0) is True
+        assert is_finite(1) is True
+        assert is_finite(-1) is True
+        assert is_finite(1 >> 100) is True
+
+    def floats_are_finite():
+        assert is_finite(0.0) is True
+        assert is_finite(1.5) is True
+        assert is_finite(-1.5) is True
+        assert is_finite(1e100) is True
+        assert is_finite(-1e100) is True
+        assert is_finite(1e-100) is True
+
+    def nan_is_not_finite():
+        assert is_finite(nan) is False
+
+    def inf_is_not_finite():
+        assert is_finite(inf) is False
+        assert is_finite(-inf) is False
+
+    def undefined_is_not_finite():
+        assert is_finite(Undefined) is False
diff --git a/tests/pyutils/test_is_integer.py b/tests/pyutils/test_is_integer.py
new file mode 100644
index 0000000..db1bac0
--- /dev/null
+++ b/tests/pyutils/test_is_integer.py
@@ -0,0 +1,68 @@
+from math import inf, nan
+
+from graphql.pyutils import is_integer, Undefined
+
+
+def describe_is_integer():
+    def null_is_not_integer():
+        assert is_integer(None) is False
+
+    def object_is_not_integer():
+        assert is_integer(object()) is False
+
+    def booleans_are_not_integer():
+        assert is_integer(False) is False
+        assert is_integer(True) is False
+
+    def strings_are_not_integer():
+        assert is_integer("string") is False
+
+    def ints_are_integer():
+        assert is_integer(0) is True
+        assert is_integer(1) is True
+        assert is_integer(-1) is True
+        assert is_integer(42) is True
+        assert is_integer(1234567890) is True
+        assert is_integer(-1234567890) is True
+        assert is_integer(1 >> 100) is True
+
+    def floats_with_fractional_part_are_not_integer():
+        assert is_integer(0.5) is False
+        assert is_integer(1.5) is False
+        assert is_integer(-1.5) is False
+        assert is_integer(0.00001) is False
+        assert is_integer(-0.00001) is False
+        assert is_integer(1.00001) is False
+        assert is_integer(-1.00001) is False
+        assert is_integer(42.5) is False
+        assert is_integer(10000.1) is False
+        assert is_integer(-10000.1) is False
+        assert is_integer(1234567890.5) is False
+        assert is_integer(-1234567890.5) is False
+
+    def floats_without_fractional_part_are_integer():
+        assert is_integer(0.0) is True
+        assert is_integer(1.0) is True
+        assert is_integer(-1.0) is True
+        assert is_integer(10.0) is True
+        assert is_integer(-10.0) is True
+        assert is_integer(42.0) is True
+        assert is_integer(1234567890.0) is True
+        assert is_integer(-1234567890.0) is True
+        assert is_integer(1e100) is True
+        assert is_integer(-1e100) is True
+
+    def complex_is_not_integer():
+        assert is_integer(1j) is False
+        assert is_integer(-1j) is False
+        assert is_integer(42 + 1j) is False
+
+    def nan_is_not_integer():
+        assert is_integer(nan) is False
+
+    def inf_is_not_integer():
+        assert is_integer(inf) is False
+        assert is_integer(-inf) is False
+
+    def undefined_is_not_integer():
+        assert is_integer(Undefined) is False
diff --git a/tests/pyutils/test_path.py b/tests/pyutils/test_path.py
new file mode 100644
index 0000000..268d033
--- /dev/null
+++ b/tests/pyutils/test_path.py
@@ -0,0 +1,28 @@
+from graphql.pyutils import Path
+
+
+def describe_path():
+    def add_path():
+        path = Path(None, 0)
+        assert path.prev is None
+        assert path.key == 0
+        prev, path = path, Path(path, 1)
+        assert path.prev is prev
+        assert path.key == 1
+        prev, path = path, Path(path, "two")
+        assert path.prev is prev
+        assert path.key == "two"
+
+    def add_key():
+        prev = Path(None, 0)
+        path = prev.add_key("one")
+        assert path.prev is prev
+        assert path.key == "one"
+
+    def as_list():
+        path = Path(None, 1)
+        assert path.as_list() == [1]
+        path = path.add_key("two")
+        assert path.as_list() == [1, "two"]
+        path = path.add_key(3)
+        assert path.as_list() == [1, "two", 3]
diff --git a/tests/pyutils/test_print_path_list.py b/tests/pyutils/test_print_path_list.py
new file mode 100644
index 0000000..8059154
--- /dev/null
+++ b/tests/pyutils/test_print_path_list.py
@@ -0,0 +1,14 @@
+from graphql.pyutils import print_path_list
+
+
+def describe_print_path_as_list():
+    def without_key():
+        assert print_path_list([]) == ""
+
+    def with_one_key():
+        assert print_path_list(["one"]) == ".one"
+        assert print_path_list([1]) == "[1]"
+
+    def with_three_keys():
+        assert print_path_list([0, "one", 2]) == "[0].one[2]"
+        assert print_path_list(["one", 2, "three"]) == ".one[2].three"
diff --git a/tests/pyutils/test_suggestion_list.py b/tests/pyutils/test_suggestion_list.py
new file mode 100644
index 0000000..fc27ba2
--- /dev/null
+++ b/tests/pyutils/test_suggestion_list.py
@@ -0,0 +1,59 @@
+from typing import List
+
+from graphql.pyutils import suggestion_list
+
+
+def expect_suggestions(input: str, options: List[str], expected: List[str]) -> None:
+    assert suggestion_list(input, options) == expected
+
+
+def describe_suggestion_list():
+    def returns_results_when_input_is_empty():
+        expect_suggestions("", ["a"], ["a"])
+
+    def returns_empty_array_when_there_are_no_options():
+        expect_suggestions("input", [], [])
+
+    def returns_options_with_small_lexical_distance():
+        expect_suggestions("greenish", ["green"], ["green"])
+        expect_suggestions("green", ["greenish"], ["greenish"])
+
+    def rejects_options_with_distance_that_exceeds_threshold():
+        expect_suggestions("aaaa", ["aaab"], ["aaab"])
+        expect_suggestions("aaaa", ["aabb"], ["aabb"])
+        expect_suggestions("aaaa", ["abbb"], [])
+
+        expect_suggestions("ab", ["ca"], [])
+
+    def returns_options_with_different_case():
+        expect_suggestions("verylongstring", ["VERYLONGSTRING"], ["VERYLONGSTRING"])
+
+        expect_suggestions("VERYLONGSTRING", ["verylongstring"], ["verylongstring"])
+
+        expect_suggestions("VERYLONGSTRING", ["VeryLongString"], ["VeryLongString"])
+
+    def returns_options_with_transpositions():
+        expect_suggestions("agr", ["arg"], ["arg"])
+
+        expect_suggestions("214365879", ["123456789"], ["123456789"])
+
+    def returns_options_sorted_based_on_lexical_distance():
+        expect_suggestions("abc", ["a", "ab", "abc"], ["abc", "ab", "a"])
+
+        expect_suggestions(
+            "GraphQl",
+            ["graphics", "SQL", "GraphQL", "quarks", "mark"],
+            ["GraphQL", "graphics"],
+        )
+
+    def returns_options_with_the_same_lexical_distance_sorted_lexicographically():
+        expect_suggestions("a", ["az", "ax", "ay"], ["ax", "ay", "az"])
+
+        expect_suggestions("boo", ["moo", "foo", "zoo"], ["foo", "moo", "zoo"])
+
+    def returns_options_sorted_first_by_lexical_distance_then_lexicographically():
+        expect_suggestions(
+            "csutomer",
+            ["store", "customer", "stomer", "some", "more"],
+            ["customer", "stomer", "some", "store"],
+        )
diff --git a/tests/pyutils/test_undefined.py b/tests/pyutils/test_undefined.py
new file mode 100644
index 0000000..b7ad8cf
--- /dev/null
+++ b/tests/pyutils/test_undefined.py
@@ -0,0 +1,28 @@
+from graphql.pyutils import Undefined
+
+
+def describe_invalid():
+    def has_repr():
+        assert repr(Undefined) == "Undefined"
+
+    def has_str():
+        assert str(Undefined) == "Undefined"
+
+    def is_hashable():
+        assert hash(Undefined) == hash(Undefined)
+        assert hash(Undefined) != hash(None)
+        assert hash(Undefined) != hash(False)
+        assert hash(Undefined) != hash(True)
+
+    def as_bool_is_false():
+        assert bool(Undefined) is False
+
+    def only_equal_to_itself():
+        assert Undefined == Undefined
+        assert not Undefined != Undefined
+        none_object = None
+        assert Undefined != none_object
+        assert not Undefined == none_object
+        false_object = False
+        assert Undefined != false_object
+        assert not Undefined == false_object
diff --git a/tests/star_wars_data.py b/tests/star_wars_data.py
new file mode 100644
index 0000000..d294b7f
--- /dev/null
+++ b/tests/star_wars_data.py
@@ -0,0 +1,141 @@
+"""This defines a basic set of data for our Star Wars Schema.
+
+This data is hard coded for the sake of the demo, but you could imagine fetching this
+data from a backend service rather than from hardcoded JSON objects in a more complex
+demo.
+"""
+
+from typing import Awaitable, Collection, Iterator
+
+__all__ = ["get_droid", "get_friends", "get_hero", "get_human", "get_secret_backstory"]
+
+# These are classes which correspond to the schema.
+# They represent the shape of the data visited during field resolution.
+
+
+class Character:
+    id: str
+    name: str
+    friends: Collection[str]
+    appearsIn: Collection[str]
+
+
+# noinspection PyPep8Naming
+class Human(Character):
+    type = "Human"
+    homePlanet: str
+
+    # noinspection PyShadowingBuiltins
+    def __init__(self, id, name, friends, appearsIn, homePlanet):
+        self.id, self.name = id, name
+        self.friends, self.appearsIn = friends, appearsIn
+        self.homePlanet = homePlanet
+
+
+# noinspection PyPep8Naming
+class Droid(Character):
+    type = "Droid"
+    primaryFunction: str
+
+    # noinspection PyShadowingBuiltins
+    def __init__(self, id, name, friends, appearsIn, primaryFunction):
+        self.id, self.name = id, name
+        self.friends, self.appearsIn = friends, appearsIn
+        self.primaryFunction = primaryFunction
+
+
+luke = Human(
+    id="1000",
+    name="Luke Skywalker",
+    friends=["1002", "1003", "2000", "2001"],
+    appearsIn=[4, 5, 6],
+    homePlanet="Tatooine",
+)
+
+vader = Human(
+    id="1001",
+    name="Darth Vader",
+    friends=["1004"],
+    appearsIn=[4, 5, 6],
+    homePlanet="Tatooine",
+)
+
+han = Human(
+    id="1002",
+    name="Han Solo",
+    friends=["1000", "1003", "2001"],
+    appearsIn=[4, 5, 6],
+    homePlanet=None,
+)
+
+leia = Human(
+    id="1003",
+    name="Leia Organa",
+    friends=["1000", "1002", "2000", "2001"],
+    appearsIn=[4, 5, 6],
+    homePlanet="Alderaan",
+)
+
+tarkin = Human(
+    id="1004", name="Wilhuff Tarkin", friends=["1001"], appearsIn=[4], homePlanet=None
+)
+
+human_data = {"1000": luke, "1001": vader, "1002": han, "1003": leia, "1004": tarkin}
+
+threepio = Droid(
+    id="2000",
+    name="C-3PO",
+    friends=["1000", "1002", "1003", "2001"],
+    appearsIn=[4, 5, 6],
+    primaryFunction="Protocol",
+)
+
+artoo = Droid(
+    id="2001",
+    name="R2-D2",
+    friends=["1000", "1002", "1003"],
+    appearsIn=[4, 5, 6],
+    primaryFunction="Astromech",
+)
+
+droid_data = {"2000": threepio, "2001": artoo}
+
+
+# noinspection PyShadowingBuiltins
+async def get_character(id: str) -> Character:
+    """Helper function to get a character by ID."""
+    # We use an async function just to illustrate that GraphQL-core supports it.
+    return human_data.get(id) or droid_data.get(id)  # type: ignore
+
+
+def get_friends(character: Character) -> Iterator[Awaitable[Character]]:
+    """Allows us to query for a character's friends."""
+    # Notice that GraphQL-core accepts iterators of awaitables.
+    return map(get_character, character.friends)
+
+
+def get_hero(episode: int) -> Character:
+    """Allows us to fetch the undisputed hero of the trilogy, R2-D2."""
+    if episode == 5:
+        # Luke is the hero of Episode V.
+        return luke
+    # Artoo is the hero otherwise.
+    return artoo
+
+
+# noinspection PyShadowingBuiltins
+def get_human(id: str) -> Human:
+    """Allows us to query for the human with the given id."""
+    return human_data.get(id)  # type: ignore
+
+
+# noinspection PyShadowingBuiltins
+def get_droid(id: str) -> Droid:
+    """Allows us to query for the droid with the given id."""
+    return droid_data.get(id)  # type: ignore
+
+
+# noinspection PyUnusedLocal
+def get_secret_backstory(character: Character) -> str:
+    """Raise an error when attempting to get the secret backstory."""
+    raise RuntimeError("secretBackstory is secret.")
diff --git a/tests/star_wars_schema.py b/tests/star_wars_schema.py
new file mode 100644
index 0000000..123ec68
--- /dev/null
+++ b/tests/star_wars_schema.py
@@ -0,0 +1,258 @@
+"""Star Wars GraphQL schema
+
+This is designed to be an end-to-end test, demonstrating the full GraphQL stack.
+
+We will create a GraphQL schema that describes the major characters in the original
+Star Wars trilogy.
+
+NOTE: This may contain spoilers for the original Star Wars trilogy.
+
+Using our shorthand to describe type systems, the type system for our Star Wars example
+is::
+
+    enum Episode { NEW_HOPE, EMPIRE, JEDI }
+
+    interface Character {
+      id: String!
+      name: String
+      friends: [Character]
+      appearsIn: [Episode]
+    }
+
+    type Human implements Character {
+      id: String!
+      name: String
+      friends: [Character]
+      appearsIn: [Episode]
+      homePlanet: String
+    }
+
+    type Droid implements Character {
+      id: String!
+      name: String
+      friends: [Character]
+      appearsIn: [Episode]
+      primaryFunction: String
+    }
+
+    type Query {
+      hero(episode: Episode): Character
+      human(id: String!): Human
+      droid(id: String!): Droid
+    }
+"""
+
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+from tests.star_wars_data import (
+    get_droid,
+    get_friends,
+    get_hero,
+    get_human,
+    get_secret_backstory,
+)
+
+__all__ = ["star_wars_schema"]
+
+# We begin by setting up our schema.
+
+# The original trilogy consists of three movies.
+#
+# This implements the following type system shorthand:
+#   enum Episode { NEW_HOPE, EMPIRE, JEDI }
+
+episode_enum = GraphQLEnumType(
+    "Episode",
+    {
+        "NEW_HOPE": GraphQLEnumValue(4, description="Released in 1977."),
+        "EMPIRE": GraphQLEnumValue(5, description="Released in 1980."),
+        "JEDI": GraphQLEnumValue(6, description="Released in 1983."),
+    },
+    description="One of the films in the Star Wars Trilogy",
+)
+
+# Characters in the Star Wars trilogy are either humans or droids.
+#
+# This implements the following type system shorthand:
+#   interface Character {
+#     id: String!
+#     name: String
+#     friends: [Character]
+#     appearsIn: [Episode]
+#     secretBackstory: String
+
+human_type: GraphQLObjectType
+droid_type: GraphQLObjectType
+
+character_interface: GraphQLInterfaceType = GraphQLInterfaceType(
+    "Character",
+    lambda: {
+        "id": GraphQLField(
+            GraphQLNonNull(GraphQLString), description="The id of the character."
+        ),
+        "name": GraphQLField(GraphQLString, description="The name of the character."),
+        "friends": GraphQLField(
+            GraphQLList(character_interface),
+            description="The friends of the character,"
+            " or an empty list if they have none.",
+        ),
+        "appearsIn": GraphQLField(
+            GraphQLList(episode_enum), description="Which movies they appear in."
+        ),
+        "secretBackstory": GraphQLField(
+            GraphQLString, description="All secrets about their past."
+        ),
+    },
+    resolve_type=lambda character, _info, _type: {
+        "Human": human_type,
+        "Droid": droid_type,
+    }[character.type],
+    description="A character in the Star Wars Trilogy",
+)
+
+# We define our human type, which implements the character interface.
+#
+# This implements the following type system shorthand:
+#   type Human : Character {
+#     id: String!
+#     name: String
+#     friends: [Character]
+#     appearsIn: [Episode]
+#     secretBackstory: String
+#   }
+
+human_type = GraphQLObjectType(
+    "Human",
+    lambda: {
+        "id": GraphQLField(
+            GraphQLNonNull(GraphQLString), description="The id of the human."
+        ),
+        "name": GraphQLField(GraphQLString, description="The name of the human."),
+        "friends": GraphQLField(
+            GraphQLList(character_interface),
+            description="The friends of the human,"
+            " or an empty list if they have none.",
+            resolve=lambda human, _info: get_friends(human),
+        ),
+        "appearsIn": GraphQLField(
+            GraphQLList(episode_enum), description="Which movies they appear in."
+        ),
+        "homePlanet": GraphQLField(
+            GraphQLString,
+            description="The home planet of the human, or null if unknown.",
+        ),
+        "secretBackstory": GraphQLField(
+            GraphQLString,
+            resolve=lambda human, _info: get_secret_backstory(human),
+            description="Where are they from and how they came to be who they are.",
+        ),
+    },
+    interfaces=[character_interface],
+    description="A humanoid creature in the Star Wars universe.",
+)
+
+# The other type of character in Star Wars is a droid.
+#
+# This implements the following type system shorthand:
+#   type Droid : Character {
+#     id: String!
+#     name: String
+#     friends: [Character]
+#     appearsIn: [Episode]
+#     secretBackstory: String
+#     primaryFunction: String
+#   }
+
+droid_type = GraphQLObjectType(
+    "Droid",
+    lambda: {
+        "id": GraphQLField(
+            GraphQLNonNull(GraphQLString), description="The id of the droid."
+        ),
+        "name": GraphQLField(GraphQLString, description="The name of the droid."),
+        "friends": GraphQLField(
+            GraphQLList(character_interface),
+            description="The friends of the droid,"
+            " or an empty list if they have none.",
+            resolve=lambda droid, _info: get_friends(droid),
+        ),
+        "appearsIn": GraphQLField(
+            GraphQLList(episode_enum), description="Which movies they appear in."
+        ),
+        "secretBackstory": GraphQLField(
+            GraphQLString,
+            resolve=lambda droid, _info: get_secret_backstory(droid),
+            description="Construction date and the name of the designer.",
+        ),
+        "primaryFunction": GraphQLField(
+            GraphQLString, description="The primary function of the droid."
+        ),
+    },
+    interfaces=[character_interface],
+    description="A mechanical creature in the Star Wars universe.",
+)
+
+# This is the type that will be the root of our query, and the
+# entry point into our schema. It gives us the ability to fetch
+# objects by their IDs, as well as to fetch the undisputed hero
+# of the Star Wars trilogy, R2-D2, directly.
+#
+# This implements the following type system shorthand:
+#   type Query {
+#     hero(episode: Episode): Character
+#     human(id: String!): Human
+#     droid(id: String!): Droid
+#   }
+
+# noinspection PyShadowingBuiltins
+query_type = GraphQLObjectType(
+    "Query",
+    lambda: {
+        "hero": GraphQLField(
+            character_interface,
+            args={
+                "episode": GraphQLArgument(
+                    episode_enum,
+                    description=(
+                        "If omitted, returns the hero of the whole saga."
+                        " If provided, returns the hero of that particular episode."
+                    ),
+                )
+            },
+            resolve=lambda _source, _info, episode=None: get_hero(episode),
+        ),
+        "human": GraphQLField(
+            human_type,
+            args={
+                "id": GraphQLArgument(
+                    GraphQLNonNull(GraphQLString), description="id of the human"
+                )
+            },
+            resolve=lambda _source, _info, id: get_human(id),
+        ),
+        "droid": GraphQLField(
+            droid_type,
+            args={
+                "id": GraphQLArgument(
+                    GraphQLNonNull(GraphQLString), description="id of the droid"
+                )
+            },
+            resolve=lambda _source, _info, id: get_droid(id),
+        ),
+    },
+)
+
+# Finally, we construct our schema (whose starting query type is the query
+# type we defined above) and export it.
+
+star_wars_schema = GraphQLSchema(query_type, types=[human_type, droid_type])
diff --git a/tests/starwars/__init__.py b/tests/starwars/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/starwars/starwars_fixtures.py b/tests/starwars/starwars_fixtures.py
deleted file mode 100644
index 9007b1a..0000000
--- a/tests/starwars/starwars_fixtures.py
+++ /dev/null
@@ -1,83 +0,0 @@
-from collections import namedtuple
-
-Human = namedtuple("Human", "id name friends appearsIn homePlanet")
-
-luke = Human(
-    id="1000",
-    name="Luke Skywalker",
-    friends=["1002", "1003", "2000", "2001"],
-    appearsIn=[4, 5, 6],
-    homePlanet="Tatooine",
-)
-
-vader = Human(
-    id="1001",
-    name="Darth Vader",
-    friends=["1004"],
-    appearsIn=[4, 5, 6],
-    homePlanet="Tatooine",
-)
-
-han = Human(
-    id="1002",
-    name="Han Solo",
-    friends=["1000", "1003", "2001"],
-    appearsIn=[4, 5, 6],
-    homePlanet=None,
-)
-
-leia = Human(
-    id="1003",
-    name="Leia Organa",
-    friends=["1000", "1002", "2000", "2001"],
-    appearsIn=[4, 5, 6],
-    homePlanet="Alderaan",
-)
-
-tarkin = Human(
-    id="1004", name="Wilhuff Tarkin", friends=["1001"], appearsIn=[4], homePlanet=None
-)
-
-humanData = {"1000": luke, "1001": vader, "1002": han, "1003": leia, "1004": tarkin}
-
-Droid = namedtuple("Droid", "id name friends appearsIn primaryFunction")
-
-threepio = Droid(
-    id="2000",
-    name="C-3PO",
-    friends=["1000", "1002", "1003", "2001"],
-    appearsIn=[4, 5, 6],
-    primaryFunction="Protocol",
-)
-
-artoo = Droid(
-    id="2001",
-    name="R2-D2",
-    friends=["1000", "1002", "1003"],
-    appearsIn=[4, 5, 6],
-    primaryFunction="Astromech",
-)
-
-droidData = {"2000": threepio, "2001": artoo}
-
-
-def getCharacter(id):
-    return humanData.get(id) or droidData.get(id)
-
-
-def getFriends(character):
-    return map(getCharacter, character.friends)
-
-
-def getHero(episode):
-    if episode == 5:
-        return luke
-    return artoo
-
-
-def getHuman(id):
-    return humanData.get(id)
-
-
-def getDroid(id):
-    return droidData.get(id)
diff --git a/tests/starwars/starwars_schema.py b/tests/starwars/starwars_schema.py
deleted file mode 100644
index a5c2a6f..0000000
--- a/tests/starwars/starwars_schema.py
+++ /dev/null
@@ -1,128 +0,0 @@
-from graphql.type import (
-    GraphQLArgument,
-    GraphQLEnumType,
-    GraphQLEnumValue,
-    GraphQLField,
-    GraphQLInterfaceType,
-    GraphQLList,
-    GraphQLNonNull,
-    GraphQLObjectType,
-    GraphQLSchema,
-    GraphQLString,
-)
-
-from .starwars_fixtures import getDroid, getFriends, getHero, getHuman
-
-episodeEnum = GraphQLEnumType(
-    "Episode",
-    description="One of the films in the Star Wars Trilogy",
-    values={
-        "NEWHOPE": GraphQLEnumValue(4, description="Released in 1977."),
-        "EMPIRE": GraphQLEnumValue(5, description="Released in 1980."),
-        "JEDI": GraphQLEnumValue(6, description="Released in 1983."),
-    },
-)
-
-characterInterface = GraphQLInterfaceType(
-    "Character",
-    description="A character in the Star Wars Trilogy",
-    fields=lambda: {
-        "id": GraphQLField(
-            GraphQLNonNull(GraphQLString), description="The id of the character."
-        ),
-        "name": GraphQLField(GraphQLString, description="The name of the character."),
-        "friends": GraphQLField(
-            GraphQLList(characterInterface),
-            description="The friends of the character, or an empty list if they have none.",
-        ),
-        "appearsIn": GraphQLField(
-            GraphQLList(episodeEnum), description="Which movies they appear in."
-        ),
-    },
-    resolve_type=lambda character, info: humanType
-    if getHuman(character.id)
-    else droidType,
-)
-
-humanType = GraphQLObjectType(
-    "Human",
-    description="A humanoid creature in the Star Wars universe.",
-    fields=lambda: {
-        "id": GraphQLField(
-            GraphQLNonNull(GraphQLString), description="The id of the human."
-        ),
-        "name": GraphQLField(GraphQLString, description="The name of the human."),
-        "friends": GraphQLField(
-            GraphQLList(characterInterface),
-            description="The friends of the human, or an empty list if they have none.",
-            resolver=lambda human, *_: getFriends(human),
-        ),
-        "appearsIn": GraphQLField(
-            GraphQLList(episodeEnum), description="Which movies they appear in."
-        ),
-        "homePlanet": GraphQLField(
-            GraphQLString,
-            description="The home planet of the human, or null if unknown.",
-        ),
-    },
-    interfaces=[characterInterface],
-)
-
-droidType = GraphQLObjectType(
-    "Droid",
-    description="A mechanical creature in the Star Wars universe.",
-    fields=lambda: {
-        "id": GraphQLField(
-            GraphQLNonNull(GraphQLString), description="The id of the droid."
-        ),
-        "name": GraphQLField(GraphQLString, description="The name of the droid."),
-        "friends": GraphQLField(
-            GraphQLList(characterInterface),
-            description="The friends of the droid, or an empty list if they have none.",
-            resolver=lambda droid, info, **args: getFriends(droid),
-        ),
-        "appearsIn": GraphQLField(
-            GraphQLList(episodeEnum), description="Which movies they appear in."
-        ),
-        "primaryFunction": GraphQLField(
-            GraphQLString, description="The primary function of the droid."
-        ),
-    },
-    interfaces=[characterInterface],
-)
-
-queryType = GraphQLObjectType(
-    "Query",
-    fields=lambda: {
-        "hero": GraphQLField(
-            characterInterface,
-            args={
-                "episode": GraphQLArgument(
-                    description="If omitted, returns the hero of the whole saga. If "
-                    "provided, returns the hero of that particular episode.",
-                    type=episodeEnum,
-                )
-            },
-            resolver=lambda root, info, **args: getHero(args.get("episode")),
-        ),
-        "human": GraphQLField(
-            humanType,
-            args={
-                "id": GraphQLArgument(
-                    description="id of the human", type=GraphQLNonNull(GraphQLString)
-                )
-            },
-            resolver=lambda root, info, **args: getHuman(args["id"]),
-        ),
-        "droid": GraphQLField(
-            droidType,
-            args={
-                "id": GraphQLArgument(
-                    description="id of the droid", type=GraphQLNonNull(GraphQLString)
-                )
-            },
-            resolver=lambda root, info, **args: getDroid(args["id"]),
-        ),
-    },
-)
-StarWarsSchema = GraphQLSchema(query=queryType, types=[humanType, droidType])
diff --git a/tests/starwars/test_introspection.py b/tests/starwars/test_introspection.py
deleted file mode 100644
index f3e0729..0000000
--- a/tests/starwars/test_introspection.py
+++ /dev/null
@@ -1,314 +0,0 @@
-from graphql import graphql
-from graphql.error import format_error
-from graphql.pyutils.contain_subset import contain_subset
-
-from .starwars_schema import StarWarsSchema
-
-
-def test_allows_querying_the_schema_for_types():
-    query = """
-        query IntrospectionTypeQuery {
-          __schema {
-            types {
-              name
-            }
-          }
-        }
-    """
-    expected = {
-        "__schema": {
-            "types": [
-                {"name": "Query"},
-                {"name": "Episode"},
-                {"name": "Character"},
-                {"name": "String"},
-                {"name": "Human"},
-                {"name": "Droid"},
-                {"name": "__Schema"},
-                {"name": "__Type"},
-                {"name": "__TypeKind"},
-                {"name": "Boolean"},
-                {"name": "__Field"},
-                {"name": "__InputValue"},
-                {"name": "__EnumValue"},
-                {"name": "__Directive"},
-                {"name": "__DirectiveLocation"},
-            ]
-        }
-    }
-
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
-
-
-def test_allows_querying_the_schema_for_query_type():
-    query = """
-      query IntrospectionQueryTypeQuery {
-        __schema {
-          queryType {
-            name
-          }
-        }
-      }
-    """
-
-    expected = {"__schema": {"queryType": {"name": "Query"}}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
-
-
-def test_allows_querying_the_schema_for_a_specific_type():
-    query = """
-      query IntrospectionDroidTypeQuery {
-        __type(name: "Droid") {
-          name
-        }
-      }
-    """
-
-    expected = {"__type": {"name": "Droid"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
-
-
-def test_allows_querying_the_schema_for_an_object_kind():
-    query = """
-      query IntrospectionDroidKindQuery {
-        __type(name: "Droid") {
-          name
-          kind
-        }
-      }
-    """
-
-    expected = {"__type": {"name": "Droid", "kind": "OBJECT"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
-
-
-def test_allows_querying_the_schema_for_an_interface_kind():
-    query = """
-      query IntrospectionCharacterKindQuery {
-        __type(name: "Character") {
-          name
-          kind
-        }
-      }
-    """
-    expected = {"__type": {"name": "Character", "kind": "INTERFACE"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
-
-
-def test_allows_querying_the_schema_for_object_fields():
-    query = """
-      query IntrospectionDroidFieldsQuery {
-        __type(name: "Droid") {
-          name
-          fields {
-            name
-            type {
-              name
-              kind
-            }
-          }
-        }
-      }
-    """
-
-    expected = {
-        "__type": {
-            "name": "Droid",
-            "fields": [
-                {"name": "id", "type": {"name": None, "kind": "NON_NULL"}},
-                {"name": "name", "type": {"name": "String", "kind": "SCALAR"}},
-                {"name": "friends", "type": {"name": None, "kind": "LIST"}},
-                {"name": "appearsIn", "type": {"name": None, "kind": "LIST"}},
-                {
-                    "name": "primaryFunction",
-                    "type": {"name": "String", "kind": "SCALAR"},
-                },
-            ],
-        }
-    }
-
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
-
-
-def test_allows_querying_the_schema_for_nested_object_fields():
-    query = """
-      query IntrospectionDroidNestedFieldsQuery {
-        __type(name: "Droid") {
-          name
-          fields {
-            name
-            type {
-              name
-              kind
-              ofType {
-                name
-                kind
-              }
-            }
-          }
-        }
-      }
-    """
-
-    expected = {
-        "__type": {
-            "name": "Droid",
-            "fields": [
-                {
-                    "name": "id",
-                    "type": {
-                        "name": None,
-                        "kind": "NON_NULL",
-                        "ofType": {"name": "String", "kind": "SCALAR"},
-                    },
-                },
-                {
-                    "name": "name",
-                    "type": {"name": "String", "kind": "SCALAR", "ofType": None},
-                },
-                {
-                    "name": "friends",
-                    "type": {
-                        "name": None,
-                        "kind": "LIST",
-                        "ofType": {"name": "Character", "kind": "INTERFACE"},
-                    },
-                },
-                {
-                    "name": "appearsIn",
-                    "type": {
-                        "name": None,
-                        "kind": "LIST",
-                        "ofType": {"name": "Episode", "kind": "ENUM"},
-                    },
-                },
-                {
-                    "name": "primaryFunction",
-                    "type": {"name": "String", "kind": "SCALAR", "ofType": None},
-                },
-            ],
-        }
-    }
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
-
-
-def test_allows_querying_the_schema_for_field_args():
-    query = """
-      query IntrospectionQueryTypeQuery {
-        __schema {
-          queryType {
-            fields {
-              name
-              args {
-                name
-                description
-                type {
-                  name
-                  kind
-                  ofType {
-                    name
-                    kind
-                  }
-                }
-                defaultValue
-              }
-            }
-          }
-        }
-      }
-    """
-
-    expected = {
-        "__schema": {
-            "queryType": {
-                "fields": [
-                    {
-                        "name": "hero",
-                        "args": [
-                            {
-                                "defaultValue": None,
-                                "description": "If omitted, returns the hero of the whole "
-                                + "saga. If provided, returns the hero of "
-                                + "that particular episode.",
-                                "name": "episode",
-                                "type": {
-                                    "kind": "ENUM",
-                                    "name": "Episode",
-                                    "ofType": None,
-                                },
-                            }
-                        ],
-                    },
-                    {
-                        "name": "human",
-                        "args": [
-                            {
-                                "name": "id",
-                                "description": "id of the human",
-                                "type": {
-                                    "kind": "NON_NULL",
-                                    "name": None,
-                                    "ofType": {"kind": "SCALAR", "name": "String"},
-                                },
-                                "defaultValue": None,
-                            }
-                        ],
-                    },
-                    {
-                        "name": "droid",
-                        "args": [
-                            {
-                                "name": "id",
-                                "description": "id of the droid",
-                                "type": {
-                                    "kind": "NON_NULL",
-                                    "name": None,
-                                    "ofType": {"kind": "SCALAR", "name": "String"},
-                                },
-                                "defaultValue": None,
-                            }
-                        ],
-                    },
-                ]
-            }
-        }
-    }
-
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
-
-
-def test_allows_querying_the_schema_for_documentation():
-    query = """
-      query IntrospectionDroidDescriptionQuery {
-        __type(name: "Droid") {
-          name
-          description
-        }
-      }
-    """
-
-    expected = {
-        "__type": {
-            "name": "Droid",
-            "description": "A mechanical creature in the Star Wars universe.",
-        }
-    }
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert contain_subset(result.data, expected)
diff --git a/tests/starwars/test_query.py b/tests/starwars/test_query.py
deleted file mode 100644
index 5e3c2cd..0000000
--- a/tests/starwars/test_query.py
+++ /dev/null
@@ -1,283 +0,0 @@
-from graphql import graphql
-from graphql.error import format_error
-
-from .starwars_schema import StarWarsSchema
-
-
-def test_hero_name_query():
-    query = """
-        query HeroNameQuery {
-          hero {
-            name
-          }
-        }
-    """
-    expected = {"hero": {"name": "R2-D2"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_hero_name_and_friends_query():
-    query = """
-        query HeroNameAndFriendsQuery {
-          hero {
-            id
-            name
-            friends {
-              name
-            }
-          }
-        }
-    """
-    expected = {
-        "hero": {
-            "id": "2001",
-            "name": "R2-D2",
-            "friends": [
-                {"name": "Luke Skywalker"},
-                {"name": "Han Solo"},
-                {"name": "Leia Organa"},
-            ],
-        }
-    }
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_nested_query():
-    query = """
-        query NestedQuery {
-          hero {
-            name
-            friends {
-              name
-              appearsIn
-              friends {
-                name
-              }
-            }
-          }
-        }
-    """
-    expected = {
-        "hero": {
-            "name": "R2-D2",
-            "friends": [
-                {
-                    "name": "Luke Skywalker",
-                    "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
-                    "friends": [
-                        {"name": "Han Solo"},
-                        {"name": "Leia Organa"},
-                        {"name": "C-3PO"},
-                        {"name": "R2-D2"},
-                    ],
-                },
-                {
-                    "name": "Han Solo",
-                    "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
-                    "friends": [
-                        {"name": "Luke Skywalker"},
-                        {"name": "Leia Organa"},
-                        {"name": "R2-D2"},
-                    ],
-                },
-                {
-                    "name": "Leia Organa",
-                    "appearsIn": ["NEWHOPE", "EMPIRE", "JEDI"],
-                    "friends": [
-                        {"name": "Luke Skywalker"},
-                        {"name": "Han Solo"},
-                        {"name": "C-3PO"},
-                        {"name": "R2-D2"},
-                    ],
-                },
-            ],
-        }
-    }
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_fetch_luke_query():
-    query = """
-        query FetchLukeQuery {
-          human(id: "1000") {
-            name
-          }
-        }
-    """
-    expected = {"human": {"name": "Luke Skywalker"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_fetch_some_id_query():
-    query = """
-        query FetchSomeIDQuery($someId: String!) {
-          human(id: $someId) {
-            name
-          }
-        }
-    """
-    params = {"someId": "1000"}
-    expected = {"human": {"name": "Luke Skywalker"}}
-    result = graphql(StarWarsSchema, query, variable_values=params)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_fetch_some_id_query2():
-    query = """
-        query FetchSomeIDQuery($someId: String!) {
-          human(id: $someId) {
-            name
-          }
-        }
-    """
-    params = {"someId": "1002"}
-    expected = {"human": {"name": "Han Solo"}}
-    result = graphql(StarWarsSchema, query, variable_values=params)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_invalid_id_query():
-    query = """
-        query humanQuery($id: String!) {
-          human(id: $id) {
-            name
-          }
-        }
-    """
-    params = {"id": "not a valid id"}
-    expected = {"human": None}
-    result = graphql(StarWarsSchema, query, variable_values=params)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_fetch_luke_aliased():
-    query = """
-        query FetchLukeAliased {
-          luke: human(id: "1000") {
-            name
-          }
-        }
-    """
-    expected = {"luke": {"name": "Luke Skywalker"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_fetch_luke_and_leia_aliased():
-    query = """
-        query FetchLukeAndLeiaAliased {
-          luke: human(id: "1000") {
-            name
-          }
-          leia: human(id: "1003") {
-            name
-          }
-        }
-    """
-    expected = {"luke": {"name": "Luke Skywalker"}, "leia": {"name": "Leia Organa"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_duplicate_fields():
-    query = """
-        query DuplicateFields {
-          luke: human(id: "1000") {
-            name
-            homePlanet
-          }
-          leia: human(id: "1003") {
-            name
-            homePlanet
-          }
-        }
-    """
-    expected = {
-        "luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"},
-        "leia": {"name": "Leia Organa", "homePlanet": "Alderaan"},
-    }
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_use_fragment():
-    query = """
-        query UseFragment {
-          luke: human(id: "1000") {
-            ...HumanFragment
-          }
-          leia: human(id: "1003") {
-            ...HumanFragment
-          }
-        }
-        fragment HumanFragment on Human {
-          name
-          homePlanet
-        }
-    """
-    expected = {
-        "luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"},
-        "leia": {"name": "Leia Organa", "homePlanet": "Alderaan"},
-    }
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_check_type_of_r2():
-    query = """
-        query CheckTypeOfR2 {
-          hero {
-            __typename
-            name
-          }
-        }
-    """
-    expected = {"hero": {"__typename": "Droid", "name": "R2-D2"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_check_type_of_luke():
-    query = """
-        query CheckTypeOfLuke {
-          hero(episode: EMPIRE) {
-            __typename
-            name
-          }
-        }
-    """
-    expected = {"hero": {"__typename": "Human", "name": "Luke Skywalker"}}
-    result = graphql(StarWarsSchema, query)
-    assert not result.errors
-    assert result.data == expected
-
-
-def test_parse_error():
-    query = """
-        qeury
-    """
-    result = graphql(StarWarsSchema, query)
-    assert result.invalid
-    formatted_error = format_error(result.errors[0])
-    assert formatted_error["locations"] == [{"column": 9, "line": 2}]
-    assert (
-        'Syntax Error GraphQL (2:9) Unexpected Name "qeury"'
-        in formatted_error["message"]
-    )
-    assert result.data is None
diff --git a/tests/starwars/test_validation.py b/tests/starwars/test_validation.py
deleted file mode 100644
index 3e389d5..0000000
--- a/tests/starwars/test_validation.py
+++ /dev/null
@@ -1,108 +0,0 @@
-from graphql.language.parser import parse
-from graphql.language.source import Source
-from graphql.validation import validate
-
-from .starwars_schema import StarWarsSchema
-
-
-def validation_errors(query):
-    source = Source(query, "StarWars.graphql")
-    ast = parse(source)
-    return validate(StarWarsSchema, ast)
-
-
-def test_nested_query_with_fragment():
-    query = """
-        query NestedQueryWithFragment {
-          hero {
-            ...NameAndAppearances
-            friends {
-              ...NameAndAppearances
-              friends {
-                ...NameAndAppearances
-              }
-            }
-          }
-        }
-        fragment NameAndAppearances on Character {
-          name
-          appearsIn
-        }
-    """
-    assert not validation_errors(query)
-
-
-def test_non_existent_fields():
-    query = """
-        query HeroSpaceshipQuery {
-          hero {
-            favoriteSpaceship
-          }
-        }
-    """
-    assert validation_errors(query)
-
-
-def test_require_fields_on_object():
-    query = """
-        query HeroNoFieldsQuery {
-          hero
-        }
-    """
-    assert validation_errors(query)
-
-
-def test_disallows_fields_on_scalars():
-    query = """
-        query HeroFieldsOnScalarQuery {
-          hero {
-            name {
-              firstCharacterOfName
-            }
-          }
-        }
-    """
-    assert validation_errors(query)
-
-
-def test_disallows_object_fields_on_interfaces():
-    query = """
-        query DroidFieldOnCharacter {
-          hero {
-            name
-            primaryFunction
-          }
-        }
-    """
-    assert validation_errors(query)
-
-
-def test_allows_object_fields_in_fragments():
-    query = """
-        query DroidFieldInFragment {
-          hero {
-            name
-            ...DroidFields
-          }
-        }
-        fragment DroidFields on Droid {
-          primaryFunction
-        }
-    """
-    assert not validation_errors(query)
-
-
-def test_allows_object_fields_in_inline_fragments():
-    query = """
-        query DroidFieldInFragment {
-          hero {
-            name
-            ...DroidFields
-          }
-        }
-
-        fragment DroidFields on Droid {
-            primaryFunction
-        }
-    """
-    assert not validation_errors(query)
diff --git a/tests/subscription/__init__.py b/tests/subscription/__init__.py
new file mode 100644
index 0000000..8551fd4
--- /dev/null
+++ b/tests/subscription/__init__.py
@@ -0,0 +1 @@
+"""Tests for graphql.subscription"""
diff --git a/tests/subscription/test_map_async_iterator.py b/tests/subscription/test_map_async_iterator.py
new file mode 100644
index 0000000..4df371a
--- /dev/null
+++ b/tests/subscription/test_map_async_iterator.py
@@ -0,0 +1,459 @@
+import sys
+from asyncio import Event, ensure_future, sleep
+
+from pytest import mark, raises  # type: ignore
+
+from graphql.subscription.map_async_iterator import MapAsyncIterator
+
+
+async def anext(iterable):
+    """Return the next item from an async iterator."""
+    return await iterable.__anext__()
+
+
+def describe_map_async_iterator():
+    @mark.asyncio
+    async def maps_over_async_generator():
+        async def source():
+            yield 1
+            yield 2
+            yield 3
+
+        doubles = MapAsyncIterator(source(), lambda x: x + x)
+
+        assert await anext(doubles) == 2
+        assert await anext(doubles) == 4
+        assert await anext(doubles) == 6
+        with raises(StopAsyncIteration):
+            assert await anext(doubles)
+
+    @mark.asyncio
+    async def maps_over_async_iterator():
+        items = [1, 2, 3]
+
+        class Iterator:
+            def __aiter__(self):
+                return self
+
+            async def __anext__(self):
+                try:
+                    return items.pop(0)
+                except IndexError:
+                    raise StopAsyncIteration
+
+        doubles = MapAsyncIterator(Iterator(), lambda x: x + x)
+
+        values = [value async for value in doubles]
+
+        assert not items
+        assert values == [2, 4, 6]
+
+    @mark.asyncio
+    async def compatible_with_async_for():
+        async def source():
+            yield 1
+            yield 2
+            yield 3
+
+        doubles = MapAsyncIterator(source(), lambda x: x + x)
+
+        values = [value async for value in doubles]
+
+        assert values == [2, 4, 6]
+
+    @mark.asyncio
+    async def maps_over_async_values_with_async_function():
+        async def source():
+            yield 1
+            yield 2
+            yield 3
+
+        async def double(x):
+            return x + x
+
+        doubles = MapAsyncIterator(source(), double)
+
+        values = [value async for value in doubles]
+
+        assert values == [2, 4, 6]
+
+    @mark.asyncio
+    async def allows_returning_early_from_mapped_async_generator():
+        async def source():
+            yield 1
+            yield 2
+            yield 3  # pragma: no cover
+
+        doubles = MapAsyncIterator(source(), lambda x: x + x)
+
+        assert await anext(doubles) == 2
+        assert await anext(doubles) == 4
+
+        # Early return
+        await doubles.aclose()
+
+        # Subsequent next calls
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+
+    @mark.asyncio
+    async def allows_returning_early_from_mapped_async_iterator():
+        items = [1, 2, 3]
+
+        class Iterator:
+            def __aiter__(self):
+                return self
+
+            async def __anext__(self):
+                try:
+                    return items.pop(0)
+                except IndexError:  # pragma: no cover
+                    raise StopAsyncIteration
+
+        doubles = MapAsyncIterator(Iterator(), lambda x: x + x)
+
+        assert await anext(doubles) == 2
+        assert await anext(doubles) == 4
+
+        # Early return
+        await doubles.aclose()
+
+        # Subsequent next calls
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+
+    @mark.asyncio
+    async def passes_through_early_return_from_async_values():
+        async def source():
+            try:
+                yield 1
+                yield 2
+                yield 3  # pragma: no cover
+            finally:
+                yield "Done"
+                yield "Last"
+
+        doubles = MapAsyncIterator(source(), lambda x: x + x)
+
+        assert await anext(doubles) == 2
+        assert await anext(doubles) == 4
+
+        # Early return
+        await doubles.aclose()
+
+        # Subsequent next calls may yield from finally block
+        assert await anext(doubles) == "LastLast"
+        with raises(GeneratorExit):
+            assert await anext(doubles)
+
+    @mark.asyncio
+    async def allows_throwing_errors_through_async_iterators():
+        items = [1, 2, 3]
+
+        class Iterator:
+            def __aiter__(self):
+                return self
+
+            async def __anext__(self):
+                try:
+                    return items.pop(0)
+                except IndexError:  # pragma: no cover
+                    raise StopAsyncIteration
+
+        doubles = MapAsyncIterator(Iterator(), lambda x: x + x)
+
+        assert await anext(doubles) == 2
+        assert await anext(doubles) == 4
+
+        # Throw error
+        with raises(RuntimeError, match="Ouch") as exc_info:
+            await doubles.athrow(RuntimeError("Ouch"))
+
+        assert str(exc_info.value) == "Ouch"
+
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+
+    @mark.asyncio
+    async def allows_throwing_errors_with_values_through_async_iterators():
+        class Iterator:
+            def __aiter__(self):
+                return self
+
+            async def __anext__(self):
+                return 1
+
+        one = MapAsyncIterator(Iterator(), lambda x: x)
+
+        assert await anext(one) == 1
+
+        # Throw error with value passed separately
+        try:
+            raise RuntimeError("Ouch")
+        except RuntimeError as error:
+            with raises(RuntimeError, match="Ouch") as exc_info:
+                await one.athrow(error.__class__, error)
+
+            assert exc_info.value is error
+            assert exc_info.tb is error.__traceback__
+
+        with raises(StopAsyncIteration):
+            await anext(one)
+
+    @mark.asyncio
+    async def allows_throwing_errors_with_traceback_through_async_iterators():
+        class Iterator:
+            def __aiter__(self):
+                return self
+
+            async def __anext__(self):
+                return 1
+
+        one = MapAsyncIterator(Iterator(), lambda x: x)
+
+        assert await anext(one) == 1
+
+        # Throw error with traceback passed separately
+        try:
+            raise RuntimeError("Ouch")
+        except RuntimeError as error:
+            with raises(RuntimeError) as exc_info:
+                await one.athrow(error.__class__, None, error.__traceback__)
+
+            assert exc_info.tb and error.__traceback__
+            assert exc_info.tb.tb_frame is error.__traceback__.tb_frame
+
+        with raises(StopAsyncIteration):
+            await anext(one)
+
+    @mark.asyncio
+    async def passes_through_caught_errors_through_async_generators():
+        async def source():
+            try:
+                yield 1
+                yield 2
+                yield 3  # pragma: no cover
+            except Exception as e:
+                yield e
+
+        doubles = MapAsyncIterator(source(), lambda x: x + x)
+
+        assert await anext(doubles) == 2
+        assert await anext(doubles) == 4
+
+        # Throw error
+        await doubles.athrow(RuntimeError("ouch"))
+
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+
+    @mark.asyncio
+    async def does_not_normally_map_over_thrown_errors():
+        async def source():
+            yield "Hello"
+            raise RuntimeError("Goodbye")
+
+        doubles = MapAsyncIterator(source(), lambda x: x + x)
+
+        assert await anext(doubles) == "HelloHello"
+
+        with raises(RuntimeError) as exc_info:
+            await anext(doubles)
+
+        assert str(exc_info.value) == "Goodbye"
+
+    @mark.asyncio
+    async def does_not_normally_map_over_externally_thrown_errors():
+        async def source():
+            yield "Hello"
+
+        doubles = MapAsyncIterator(source(), lambda x: x + x)
+
+        assert await anext(doubles) == "HelloHello"
+
+        with raises(RuntimeError) as exc_info:
+            await doubles.athrow(RuntimeError("Goodbye"))
+
+        assert str(exc_info.value) == "Goodbye"
+
+    @mark.asyncio
+    async def maps_over_thrown_errors_if_second_callback_provided():
+        async def source():
+            yield "Hello"
+            raise RuntimeError("Goodbye")
+
+        doubles = MapAsyncIterator(source(), lambda x: x + x, lambda error: error)
+
+        assert await anext(doubles) == "HelloHello"
+
+        result = await anext(doubles)
+        assert isinstance(result, RuntimeError)
+        assert str(result) == "Goodbye"
+
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+
+    @mark.asyncio
+    async def can_use_simple_iterator_instead_of_generator():
+        async def source():
+            yield 1
+            yield 2
+            yield 3
+
+        class Source:
+            def __init__(self):
+                self.counter = 0
+
+            def __aiter__(self):
+                return self
+
+            async def __anext__(self):
+                self.counter += 1
+                if self.counter > 3:
+                    raise StopAsyncIteration
+                return self.counter
+
+        def double(x):
+            return x + x
+
+        for iterator in source, Source:
+            doubles = MapAsyncIterator(iterator(), double)
+
+            await doubles.aclose()
+
+            with raises(StopAsyncIteration):
+                await anext(doubles)
+
+            doubles = MapAsyncIterator(iterator(), double)
+
+            assert await anext(doubles) == 2
+            assert await anext(doubles) == 4
+            assert await anext(doubles) == 6
+
+            with raises(StopAsyncIteration):
+                await anext(doubles)
+
+            doubles = MapAsyncIterator(iterator(), double)
+
+            assert await anext(doubles) == 2
+            assert await anext(doubles) == 4
+
+            # Throw error
+            with raises(RuntimeError) as exc_info:
+                await doubles.athrow(RuntimeError("ouch"))
+
+            assert str(exc_info.value) == "ouch"
+
+            with raises(StopAsyncIteration):
+                await anext(doubles)
+            with raises(StopAsyncIteration):
+                await anext(doubles)
+
+            await doubles.athrow(RuntimeError("no more ouch"))
+
+            with raises(StopAsyncIteration):
+                await anext(doubles)
+
+            await doubles.aclose()
+
+            doubles = MapAsyncIterator(iterator(), double)
+
+            assert await anext(doubles) == 2
+            assert await anext(doubles) == 4
+
+            try:
+                raise ValueError("bad")
+            except ValueError:
+                tb = sys.exc_info()[2]
+
+            # Throw error
+            with raises(ValueError):
+                await doubles.athrow(ValueError, None, tb)
+
+        await sleep(0)
+
+    @mark.asyncio
+    async def stops_async_iteration_on_close():
+        async def source():
+            yield 1
+            await Event().wait()  # Block forever
+            yield 2  # pragma: no cover
+            yield 3  # pragma: no cover
+
+        singles = source()
+        doubles = MapAsyncIterator(singles, lambda x: x * 2)
+
+        result = await anext(doubles)
+        assert result == 2
+
+        # Make sure it is blocked
+        doubles_future = ensure_future(anext(doubles))
+        await sleep(0.05)
+        assert not doubles_future.done()
+
+        # Unblock and watch StopAsyncIteration propagate
+        await doubles.aclose()
+        await sleep(0.05)
+        assert doubles_future.done()
+        assert isinstance(doubles_future.exception(), StopAsyncIteration)
+
+        with raises(StopAsyncIteration):
+            await anext(singles)
+
+    @mark.asyncio
+    async def can_unset_closed_state_of_async_iterator():
+        items = [1, 2, 3]
+
+        class Iterator:
+            def __init__(self):
+                self.is_closed = False
+
+            def __aiter__(self):
+                return self
+
+            async def __anext__(self):
+                if self.is_closed:
+                    raise StopAsyncIteration
+                try:
+                    return items.pop(0)
+                except IndexError:
+                    raise StopAsyncIteration
+
+            async def aclose(self):
+                self.is_closed = True
+
+        iterator = Iterator()
+        doubles = MapAsyncIterator(iterator, lambda x: x + x)
+
+        assert await anext(doubles) == 2
+        assert await anext(doubles) == 4
+        assert not iterator.is_closed
+        await doubles.aclose()
+        assert iterator.is_closed
+        with raises(StopAsyncIteration):
+            await anext(iterator)
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+        assert doubles.is_closed
+
+        iterator.is_closed = False
+        doubles.is_closed = False
+        assert not doubles.is_closed
+
+        assert await anext(doubles) == 6
+        assert not doubles.is_closed
+        assert not iterator.is_closed
+        with raises(StopAsyncIteration):
+            await anext(iterator)
+        with raises(StopAsyncIteration):
+            await anext(doubles)
+        assert not doubles.is_closed
+        assert not iterator.is_closed
diff --git a/tests/subscription/test_subscribe.py b/tests/subscription/test_subscribe.py
new file mode 100644
index 0000000..eab965a
--- /dev/null
+++ b/tests/subscription/test_subscribe.py
@@ -0,0 +1,911 @@
+from typing import Any, Dict
+
+from pytest import mark, raises  # type: ignore
+
+from graphql.language import parse
+from graphql.pyutils import EventEmitter, EventEmitterAsyncIterator
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLField,
+    GraphQLInt,
+    GraphQLList,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+from graphql.subscription import subscribe
+
+EmailType = GraphQLObjectType(
+    "Email",
+    {
+        "from": GraphQLField(GraphQLString),
+        "subject": GraphQLField(GraphQLString),
+        "message": GraphQLField(GraphQLString),
+        "unread": GraphQLField(GraphQLBoolean),
+    },
+)
+
+InboxType = GraphQLObjectType(
+    "Inbox",
+    {
+        "total": GraphQLField(
+            GraphQLInt, resolve=lambda inbox, _info: len(inbox["emails"])
+        ),
+        "unread": GraphQLField(
+            GraphQLInt,
+            resolve=lambda inbox, _info: sum(
+                1 for email in inbox["emails"] if email["unread"]
+            ),
+        ),
+        "emails": GraphQLField(GraphQLList(EmailType)),
+    },
+)
+
+QueryType = GraphQLObjectType("Query", {"inbox": GraphQLField(InboxType)})
+
+EmailEventType = GraphQLObjectType(
+    "EmailEvent", {"email": GraphQLField(EmailType), "inbox": GraphQLField(InboxType)}
+)
+
+
+async def anext(iterable):
+    """Return the next item from an async iterator."""
+    return await iterable.__anext__()
+
+
+def email_schema_with_resolvers(subscribe_fn=None, resolve_fn=None):
+    return GraphQLSchema(
+        query=QueryType,
+        subscription=GraphQLObjectType(
+            "Subscription",
+            {
+                "importantEmail": GraphQLField(
+                    EmailEventType,
+                    args={"priority": GraphQLArgument(GraphQLInt)},
+                    resolve=resolve_fn,
+                    subscribe=subscribe_fn,
+                )
+            },
+        ),
+    )
+
+
+email_schema = email_schema_with_resolvers()
+
+default_subscription_ast = parse(
+    """
+    subscription ($priority: Int = 0) {
+      importantEmail(priority: $priority) {
+        email {
+          from
+          subject
+        }
+        inbox {
+          unread
+          total
+        }
+      }
+    }
+    """
+)
+
+
+async def create_subscription(
+    pubsub, schema: GraphQLSchema = email_schema, document=default_subscription_ast
+):
+    data: Dict[str, Any] = {
+        "inbox": {
+            "emails": [
+                {
+                    "from": "joe@graphql.org",
+                    "subject": "Hello",
+                    "message": "Hello World",
+                    "unread": False,
+                }
+            ]
+        },
+        "importantEmail": lambda _info, priority=None: EventEmitterAsyncIterator(
+            pubsub, "importantEmail"
+        ),
+    }
+
+    def send_important_email(new_email):
+        data["inbox"]["emails"].append(new_email)
+        # Returns True if the event was consumed by a subscriber.
+        return pubsub.emit(
+            "importantEmail",
+            {"importantEmail": {"email": new_email, "inbox": data["inbox"]}},
+        )
+
+    # `subscribe` yields AsyncIterator or ExecutionResult
+    return (
+        send_important_email,
+        await subscribe(schema=schema, document=document, root_value=data),
+    )
+
+
+# Check all error cases when initializing the subscription.
+def describe_subscription_initialization_phase():
+    @mark.asyncio
+    async def accepts_positional_arguments():
+        document = parse(
+            """
+            subscription {
+              importantEmail
+            }
+            """
+        )
+
+        async def empty_async_iterator(_info):
+            for value in ():  # type: ignore
+                yield value  # pragma: no cover
+
+        ai = await subscribe(
+            email_schema, document, {"importantEmail": empty_async_iterator}
+        )
+
+        with raises(StopAsyncIteration):
+            await anext(ai)
+        await ai.aclose()  # type: ignore
+
+    @mark.asyncio
+    async def accepts_multiple_subscription_fields_defined_in_schema():
+        pubsub = EventEmitter()
+        SubscriptionTypeMultiple = GraphQLObjectType(
+            "Subscription",
+            {
+                "importantEmail": GraphQLField(EmailEventType),
+                "nonImportantEmail": GraphQLField(EmailEventType),
+            },
+        )
+
+        test_schema = GraphQLSchema(
+            query=QueryType, subscription=SubscriptionTypeMultiple
+        )
+
+        send_important_email, subscription = await create_subscription(
+            pubsub, test_schema
+        )
+
+        send_important_email(
+            {
+                "from": "yuzhi@graphql.org",
+                "subject": "Alright",
+                "message": "Tests are good",
+                "unread": True,
+            }
+        )
+
+        await anext(subscription)
+
+    @mark.asyncio
+    async def accepts_type_definition_with_sync_subscribe_function():
+        pubsub = EventEmitter()
+
+        def subscribe_email(_inbox, _info):
+            return EventEmitterAsyncIterator(pubsub, "importantEmail")
+
+        schema = GraphQLSchema(
+            query=QueryType,
+            subscription=GraphQLObjectType(
+                "Subscription",
+                {
+                    "importantEmail": GraphQLField(
+                        GraphQLString, subscribe=subscribe_email
+                    )
+                },
+            ),
+        )
+
+        subscription = await subscribe(
+            schema=schema,
+            document=parse(
+                """
+            subscription {
+              importantEmail
+            }
+            """
+            ),
+        )
+
+        pubsub.emit("importantEmail", {"importantEmail": {}})
+
+        await anext(subscription)
+
+    @mark.asyncio
+    async def accepts_type_definition_with_async_subscribe_function():
+        pubsub = EventEmitter()
+
+        async def subscribe_email(_inbox, _info):
+            return EventEmitterAsyncIterator(pubsub, "importantEmail")
+
+        schema = GraphQLSchema(
+            query=QueryType,
+            subscription=GraphQLObjectType(
+                "Subscription",
+                {
+                    "importantEmail": GraphQLField(
+                        GraphQLString, subscribe=subscribe_email
+                    )
+                },
+            ),
+        )
+
+        subscription = await subscribe(
+            schema=schema,
+            document=parse(
+                """
+            subscription {
+              importantEmail
+            }
+            """
+            ),
+        )
+
+        pubsub.emit("importantEmail", {"importantEmail": {}})
+
+        await anext(subscription)
+
+    @mark.asyncio
+    async def should_only_resolve_the_first_field_of_invalid_multi_field():
+        did_resolve = {"importantEmail": False, "nonImportantEmail": False}
+
+        def subscribe_important(_inbox, _info):
+            did_resolve["importantEmail"] = True
+            return EventEmitterAsyncIterator(EventEmitter(), "event")
+
+        def subscribe_non_important(_inbox, _info):  # pragma: no cover
+            did_resolve["nonImportantEmail"] = True
+            return EventEmitterAsyncIterator(EventEmitter(), "event")
+
+        SubscriptionTypeMultiple = GraphQLObjectType(
+            "Subscription",
+            {
+                "importantEmail": GraphQLField(
+                    EmailEventType, subscribe=subscribe_important
+                ),
+                "nonImportantEmail": GraphQLField(
+                    EmailEventType, subscribe=subscribe_non_important
+                ),
+            },
+        )
+
+        schema = GraphQLSchema(query=QueryType, subscription=SubscriptionTypeMultiple)
+
+        subscription = await subscribe(
+            schema=schema,
+            document=parse(
+                """
+            subscription {
+              importantEmail
+              nonImportantEmail
+            }
+            """
+            ),
+        )
+
+        ignored = anext(subscription)  # Ask for a result, but ignore it.
+
+        assert did_resolve["importantEmail"] is True
+        assert did_resolve["nonImportantEmail"] is False
+
+        # Close subscription
+        # noinspection PyUnresolvedReferences
+        await subscription.aclose()  # type: ignore
+
+        with raises(StopAsyncIteration):
+            await ignored
+
+    # noinspection PyArgumentList
+    @mark.asyncio
+    async def throws_an_error_if_schema_is_missing():
+        document = parse(
+            """
+            subscription {
+              importantEmail
+            }
+            """
+        )
+
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            await subscribe(None, document)  # type: ignore
+
+        assert str(exc_info.value) == "Expected None to be a GraphQL schema."
+
+        with raises(TypeError, match="missing .* positional argument: 'schema'"):
+            # noinspection PyTypeChecker
+            await subscribe(document=document)  # type: ignore
+
+    # noinspection PyArgumentList
+    @mark.asyncio
+    async def throws_an_error_if_document_is_missing():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            await subscribe(email_schema, None)  # type: ignore
+
+        assert str(exc_info.value) == "Must provide document."
+
+        with raises(TypeError, match="missing .* positional argument: 'document'"):
+            # noinspection PyTypeChecker
+            await subscribe(schema=email_schema)  # type: ignore
+
+    @mark.asyncio
+    async def resolves_to_an_error_for_unknown_subscription_field():
+        ast = parse(
+            """
+            subscription {
+              unknownField
+            }
+            """
+        )
+
+        pubsub = EventEmitter()
+
+        subscription = (await create_subscription(pubsub, document=ast))[1]
+
+        assert subscription == (
+            None,
+            [
+                {
+                    "message": "The subscription field 'unknownField' is not defined.",
+                    "locations": [(3, 15)],
+                }
+            ],
+        )
+
+    @mark.asyncio
+    async def should_pass_through_unexpected_errors_thrown_in_subscribe():
+        with raises(TypeError, match="Must provide document\\."):
+            await subscribe(schema=email_schema, document={})  # type: ignore
+
+    @mark.asyncio
+    @mark.filterwarnings("ignore:.* was never awaited:RuntimeWarning")
+    async def throws_an_error_if_subscribe_does_not_return_an_iterator():
+        invalid_email_schema = GraphQLSchema(
+            query=QueryType,
+            subscription=GraphQLObjectType(
+                "Subscription",
+                {
+                    "importantEmail": GraphQLField(
+                        GraphQLString, subscribe=lambda _inbox, _info: "test"
+                    )
+                },
+            ),
+        )
+
+        pubsub = EventEmitter()
+
+        with raises(TypeError) as exc_info:
+            await create_subscription(pubsub, invalid_email_schema)
+
+        assert str(exc_info.value) == (
+            "Subscription field must return AsyncIterable. Received: 'test'."
+        )
+
+    @mark.asyncio
+    async def resolves_to_an_error_for_subscription_resolver_errors():
+        async def test_reports_error(schema):
+            result = await subscribe(
+                schema=schema,
+                document=parse(
+                    """
+                    subscription {
+                      importantEmail
+                    }
+                    """
+                ),
+            )
+
+            assert result == (
+                None,
+                [
+                    {
+                        "message": "test error",
+                        "locations": [(3, 23)],
+                        "path": ["importantEmail"],
+                    }
+                ],
+            )
+
+        # Returning an error
+        def return_error(*_args):
+            return TypeError("test error")
+
+        subscription_returning_error_schema = email_schema_with_resolvers(return_error)
+        await test_reports_error(subscription_returning_error_schema)
+
+        # Throwing an error
+        def throw_error(*_args):
+            raise TypeError("test error")
+
+        subscription_throwing_error_schema = email_schema_with_resolvers(throw_error)
+        await test_reports_error(subscription_throwing_error_schema)
+
+        # Resolving to an error
+        async def resolve_error(*_args):
+            return TypeError("test error")
+
+        subscription_resolving_error_schema = email_schema_with_resolvers(resolve_error)
+        await test_reports_error(subscription_resolving_error_schema)
+
+        # Rejecting with an error
+        async def reject_error(*_args):
+            return TypeError("test error")
+
+        subscription_rejecting_error_schema = email_schema_with_resolvers(reject_error)
+        await test_reports_error(subscription_rejecting_error_schema)
+
+    @mark.asyncio
+    async def resolves_to_an_error_if_variables_were_wrong_type():
+        # If we receive variables that cannot be coerced correctly, subscribe() will
+        # resolve to an ExecutionResult that contains an informative error description.
+        ast = parse(
+            """
+            subscription ($priority: Int) {
+              importantEmail(priority: $priority) {
+                email {
+                  from
+                  subject
+                }
+                inbox {
+                  unread
+                  total
+                }
+              }
+            }
+            """
+        )
+
+        result = await subscribe(
+            schema=email_schema, document=ast, variable_values={"priority": "meow"},
+        )
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Variable '$priority' got invalid value 'meow';"
+                    " Int cannot represent non-integer value: 'meow'",
+                    "locations": [(2, 27)],
+                }
+            ],
+        )
+
+        assert result.errors[0].original_error is None  # type: ignore
+
+
+# Once a subscription returns a valid AsyncIterator, it can still yield errors.
+def describe_subscription_publish_phase():
+    @mark.asyncio
+    async def produces_a_payload_for_multiple_subscribe_in_same_subscription():
+        pubsub = EventEmitter()
+        send_important_email, subscription = await create_subscription(pubsub)
+        second = await create_subscription(pubsub)
+
+        payload1 = anext(subscription)
+        payload2 = anext(second[1])
+
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Alright",
+                    "message": "Tests are good",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        expected_payload = {
+            "importantEmail": {
+                "email": {"from": "yuzhi@graphql.org", "subject": "Alright"},
+                "inbox": {"unread": 1, "total": 2},
+            }
+        }
+
+        assert await payload1 == (expected_payload, None)
+        assert await payload2 == (expected_payload, None)
+
+    @mark.asyncio
+    async def produces_a_payload_per_subscription_event():
+        pubsub = EventEmitter()
+        send_important_email, subscription = await create_subscription(pubsub)
+
+        # Wait for the next subscription payload.
+        payload = anext(subscription)
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Alright",
+                    "message": "Tests are good",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        # The previously waited on payload now has a value.
+        assert await payload == (
+            {
+                "importantEmail": {
+                    "email": {"from": "yuzhi@graphql.org", "subject": "Alright"},
+                    "inbox": {"unread": 1, "total": 2},
+                }
+            },
+            None,
+        )
+
+        # Another new email arrives, before subscription.___anext__ is called.
+        assert (
+            send_important_email(
+                {
+                    "from": "hyo@graphql.org",
+                    "subject": "Tools",
+                    "message": "I <3 making things",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        # The next waited on payload will have a value.
+        assert await anext(subscription) == (
+            {
+                "importantEmail": {
+                    "email": {"from": "hyo@graphql.org", "subject": "Tools"},
+                    "inbox": {"unread": 2, "total": 3},
+                }
+            },
+            None,
+        )
+
+        # The client decides to disconnect.
+        # noinspection PyUnresolvedReferences
+        await subscription.aclose()
+
+        # Which may result in disconnecting upstream services as well.
+        assert (
+            send_important_email(
+                {
+                    "from": "adam@graphql.org",
+                    "subject": "Important",
+                    "message": "Read me please",
+                    "unread": True,
+                }
+            )
+            is False
+        )  # No more listeners.
+
+        # Awaiting subscription after closing it results in completed results.
+        with raises(StopAsyncIteration):
+            assert await anext(subscription)
+
+    @mark.asyncio
+    async def produces_a_payload_when_there_are_multiple_events():
+        pubsub = EventEmitter()
+        send_important_email, subscription = await create_subscription(pubsub)
+        payload = anext(subscription)
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Alright",
+                    "message": "Tests are good",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        assert await payload == (
+            {
+                "importantEmail": {
+                    "email": {"from": "yuzhi@graphql.org", "subject": "Alright"},
+                    "inbox": {"unread": 1, "total": 2},
+                }
+            },
+            None,
+        )
+
+        payload = anext(subscription)
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Alright 2",
+                    "message": "Tests are good 2",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        assert await payload == (
+            {
+                "importantEmail": {
+                    "email": {"from": "yuzhi@graphql.org", "subject": "Alright 2"},
+                    "inbox": {"unread": 2, "total": 3},
+                }
+            },
+            None,
+        )
+
+    @mark.asyncio
+    async def should_not_trigger_when_subscription_is_already_done():
+        pubsub = EventEmitter()
+        send_important_email, subscription = await create_subscription(pubsub)
+        payload = anext(subscription)
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Alright",
+                    "message": "Tests are good",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        assert await payload == (
+            {
+                "importantEmail": {
+                    "email": {"from": "yuzhi@graphql.org", "subject": "Alright"},
+                    "inbox": {"unread": 1, "total": 2},
+                }
+            },
+            None,
+        )
+
+        payload = anext(subscription)
+        await subscription.aclose()
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Alright 2",
+                    "message": "Tests are good 2",
+                    "unread": True,
+                }
+            )
+            is False
+        )
+
+        with raises(StopAsyncIteration):
+            await payload
+
+    @mark.asyncio
+    async def should_not_trigger_when_subscription_is_thrown():
+        pubsub = EventEmitter()
+        send_important_email, subscription = await create_subscription(pubsub)
+        payload = anext(subscription)
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Alright",
+                    "message": "Tests are good",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        assert await payload == (
+            {
+                "importantEmail": {
+                    "email": {"from": "yuzhi@graphql.org", "subject": "Alright"},
+                    "inbox": {"unread": 1, "total": 2},
+                }
+            },
+            None,
+        )
+
+        payload = anext(subscription)
+
+        # Throw error
+        with raises(RuntimeError) as exc_info:
+            await subscription.athrow(RuntimeError("ouch"))
+        assert str(exc_info.value) == "ouch"
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Alright 2",
+                    "message": "Tests are good 2",
+                    "unread": True,
+                }
+            )
+            is False
+        )
+
+        with raises(StopAsyncIteration):
+            await payload
+
+    @mark.asyncio
+    async def event_order_is_correct_for_multiple_publishes():
+        pubsub = EventEmitter()
+        send_important_email, subscription = await create_subscription(pubsub)
+
+        payload = anext(subscription)
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Message",
+                    "message": "Tests are good",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        # A new email arrives!
+        assert (
+            send_important_email(
+                {
+                    "from": "yuzhi@graphql.org",
+                    "subject": "Message 2",
+                    "message": "Tests are good 2",
+                    "unread": True,
+                }
+            )
+            is True
+        )
+
+        assert await payload == (
+            {
+                "importantEmail": {
+                    "email": {"from": "yuzhi@graphql.org", "subject": "Message"},
+                    "inbox": {"unread": 2, "total": 3},
+                }
+            },
+            None,
+        )
+
+        payload = subscription.__anext__()
+
+        assert await payload == (
+            {
+                "importantEmail": {
+                    "email": {"from": "yuzhi@graphql.org", "subject": "Message 2"},
+                    "inbox": {"unread": 2, "total": 3},
+                }
+            },
+            None,
+        )
+
+    @mark.asyncio
+    async def should_handle_error_during_execution_of_source_event():
+        async def subscribe_fn(_data, _info):
+            yield {"email": {"subject": "Hello"}}
+            yield {"email": {"subject": "Goodbye"}}
+            yield {"email": {"subject": "Bonjour"}}
+
+        def resolve_fn(event, _info):
+            if event["email"]["subject"] == "Goodbye":
+                raise RuntimeError("Never leave")
+            return event
+
+        erroring_email_schema = email_schema_with_resolvers(subscribe_fn, resolve_fn)
+
+        subscription = await subscribe(
+            erroring_email_schema,
+            parse(
+                """
+                subscription {
+                  importantEmail {
+                    email {
+                      subject
+                    }
+                  }
+                }
+                """
+            ),
+        )
+
+        payload1 = await anext(subscription)
+        assert payload1 == ({"importantEmail": {"email": {"subject": "Hello"}}}, None)
+
+        # An error in execution is presented as such.
+        payload2 = await anext(subscription)
+        assert payload2 == (
+            {"importantEmail": None},
+            [
+                {
+                    "message": "Never leave",
+                    "locations": [(3, 19)],
+                    "path": ["importantEmail"],
+                }
+            ],
+        )
+
+        # However that does not close the response event stream. Subsequent events are
+        # still executed.
+        payload3 = await anext(subscription)
+        assert payload3 == ({"importantEmail": {"email": {"subject": "Bonjour"}}}, None)
+
+    @mark.asyncio
+    async def should_pass_through_error_thrown_in_source_event_stream():
+        async def subscribe_fn(_data, _info):
+            yield {"email": {"subject": "Hello"}}
+            raise RuntimeError("test error")
+
+        def resolve_fn(event, _info):
+            return event
+
+        erroring_email_schema = email_schema_with_resolvers(subscribe_fn, resolve_fn)
+
+        subscription = await subscribe(
+            schema=erroring_email_schema,
+            document=parse(
+                """
+                subscription {
+                  importantEmail {
+                    email {
+                      subject
+                    }
+                  }
+                }
+                """
+            ),
+        )
+
+        payload1 = await anext(subscription)
+        assert payload1 == ({"importantEmail": {"email": {"subject": "Hello"}}}, None)
+
+        with raises(RuntimeError) as exc_info:
+            await anext(subscription)
+
+        assert str(exc_info.value) == "test error"
+
+        with raises(StopAsyncIteration):
+            await anext(subscription)
+
+    @mark.asyncio
+    async def should_work_with_async_resolve_function():
+        async def subscribe_fn(_data, _info):
+            yield {"email": {"subject": "Hello"}}
+
+        async def resolve_fn(event, _info):
+            return event
+
+        async_email_schema = email_schema_with_resolvers(subscribe_fn, resolve_fn)
+
+        subscription = await subscribe(
+            schema=async_email_schema,
+            document=parse(
+                """
+                subscription {
+                  importantEmail {
+                    email {
+                      subject
+                    }
+                  }
+                }
+                """
+            ),
+        )
+
+        payload = await anext(subscription)
+        assert payload == ({"importantEmail": {"email": {"subject": "Hello"}}}, None)
diff --git a/tests/test_docs.py b/tests/test_docs.py
new file mode 100644
index 0000000..7cf133e
--- /dev/null
+++ b/tests/test_docs.py
@@ -0,0 +1,350 @@
+"""Test all code snippets in the documentation"""
+
+from pathlib import Path
+from typing import Any, Dict, List
+
+from .utils import dedent
+
+Scope = Dict[str, Any]
+
+
+def get_snippets(source, indent=4):
+    """Get all code snippets from a given documentation source file."""
+    if not source.endswith(".rst"):  # pragma: no cover
+        source += ".rst"
+    source_path = Path(__file__).parents[1] / "docs" / source
+    lines = open(source_path).readlines()
+    snippets: List[str] = []
+    snippet: List[str] = []
+    snippet_start = " " * indent
+    for line in lines:
+        if not line.rstrip() and snippet:
+            snippet.append(line)
+        elif line.startswith(snippet_start):
+            snippet.append(line[indent:])
+        else:
+            if snippet:
+                snippets.append("".join(snippet).rstrip() + "\n")
+                snippet = []
+    if snippet:
+        snippets.append("".join(snippet).rstrip() + "\n")
+    return snippets
+
+
+def expected_result(snippets):
+    """Get and normalize expected result from snippet."""
+    out = snippets.pop(0)
+    assert out.startswith("ExecutionResult(")
+    return " ".join(out.split()).replace("( ", "(") + "\n"
+
+
+def expected_errors(snippets):
+    """Get and normalize expected errors from snippet."""
+    out = snippets.pop(0)
+    assert out.startswith("[GraphQLError(")
+    return " ".join(out.split()).replace("( ", "(").replace('" "', "")
+
+
+def describe_introduction():
+    def getting_started(capsys):
+        intro = get_snippets("intro")
+        pip_install = intro.pop(0)
+        assert "pip install" in pip_install and "graphql-core" in pip_install
+        pipenv_install = intro.pop(0)
+        assert "pipenv install" in pipenv_install and "graphql-core" in pipenv_install
+        create_schema = intro.pop(0)
+        assert "schema = GraphQLSchema(" in create_schema
+        scope: Scope = {}
+        exec(create_schema, scope)
+        schema = scope.get("schema")
+        schema_class = scope.get("GraphQLSchema")
+        assert schema and schema_class and isinstance(schema, schema_class)
+        query = intro.pop(0)
+        assert "graphql_sync" in query
+        exec(query, scope)
+        out, err = capsys.readouterr()
+        assert out.startswith("ExecutionResult")
+        assert not err
+        expected_out = intro.pop(0)
+        assert out == expected_out
+
+
+def describe_usage():
+    sdl = get_snippets("usage/schema")[0]
+    resolvers = get_snippets("usage/resolvers")[0]
+
+    def building_a_type_schema():
+        schema = get_snippets("usage/schema")
+        assert schema.pop(0) == sdl
+        assert "enum Episode { NEWHOPE, EMPIRE, JEDI }" in sdl
+        import_blocks = schema.pop(0)
+        assert "from graphql import" in import_blocks
+        assert "GraphQLObjectType" in import_blocks
+        scope: Scope = {}
+        exec(import_blocks, scope)
+        assert "GraphQLObjectType" in scope
+        build_enum = schema.pop(0)
+        assert "episode_enum = " in build_enum
+        exec(build_enum, scope)
+        assert scope["episode_enum"].values["EMPIRE"].value == 5
+        scope2 = scope.copy()
+        build_enum2 = schema.pop(0)
+        assert "episode_enum = " in build_enum2
+        exec(build_enum2, scope2)
+        assert scope["episode_enum"].values["EMPIRE"].value == 5
+        scope3 = scope.copy()
+        build_enum3 = schema.pop(0)
+        assert "episode_enum = " in build_enum3
+        exec(build_enum3, scope3)
+        assert scope["episode_enum"].values["EMPIRE"].value == 5
+        build_character = schema.pop(0)
+        assert "character_interface = " in build_character
+        exec(resolvers, scope)
+        exec(build_character, scope)
+        assert "character_interface" in scope
+        build_human_and_droid = schema.pop(0)
+        assert "human_type = " in build_human_and_droid
+        assert "droid_type = " in build_human_and_droid
+        exec(build_human_and_droid, scope)
+        assert "human_type" in scope
+        assert "droid_type" in scope
+        build_query_type = schema.pop(0)
+        assert "query_type = " in build_query_type
+        exec(build_query_type, scope)
+        assert "query_type" in scope
+        define_schema = schema.pop(0)
+        assert "schema = " in define_schema
+        exec(define_schema, scope)
+
+    def implementing_resolvers():
+        assert "luke = dict(" in resolvers
+        assert "def get_human(" in resolvers
+        scope: Scope = {}
+        exec(resolvers, scope)
+        get_human = scope["get_human"]
+        human = get_human(None, None, "1000")
+        assert human["name"] == "Luke Skywalker"
+
+    def executing_queries(capsys):
+        scope: Scope = {}
+        exec(resolvers, scope)
+        schema = "\n".join(get_snippets("usage/schema")[1:])
+        exec(schema, scope)
+        queries = get_snippets("usage/queries")
+
+        async_query = queries.pop(0)
+        assert "asyncio" in async_query and "graphql_sync" not in async_query
+        exec(async_query, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "R2-D2" in out
+        assert out == expected_result(queries)
+
+        sync_query = queries.pop(0)
+        assert "graphql_sync" in sync_query and "asyncio" not in sync_query
+        exec(sync_query, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "Luke" in out
+        assert out == expected_result(queries)
+
+        bad_query = queries.pop(0)
+        assert "homePlace" in bad_query
+        exec(bad_query, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "Cannot query" in out
+        assert out == expected_result(queries)
+
+        typename_query = queries.pop(0)
+        assert "__typename" in typename_query
+        exec(typename_query, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "__typename" in out and "Human" in out
+        assert out == expected_result(queries)
+
+        backstory_query = queries.pop(0)
+        assert "secretBackstory" in backstory_query
+        exec(backstory_query, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "errors" in out and "secretBackstory" in out
+        assert out == expected_result(queries)
+
+    def using_the_sdl(capsys):
+        use_sdl = get_snippets("usage/sdl")
+        build_schema = use_sdl.pop(0)
+        build_schema_sdl = dedent(
+            build_schema.partition('build_schema("""\n')[2].partition('""")')[0]
+        )
+        assert build_schema_sdl == sdl
+
+        scope: Scope = {}
+        exec(build_schema, scope)
+        schema = scope["schema"]
+        assert list(schema.query_type.fields) == ["hero", "human", "droid"]
+        exec(resolvers, scope)
+        assert schema.query_type.fields["hero"].resolve is None
+        attach_functions = use_sdl.pop(0)
+        exec(attach_functions, scope)
+        assert schema.query_type.fields["hero"].resolve is scope["get_hero"]
+        define_enum_values = use_sdl.pop(0)
+        define_episode_enum = get_snippets("usage/schema")[3]
+        define_episode_enum = define_episode_enum.partition("episode_enum =")[0]
+        assert "class EpisodeEnum" in define_episode_enum
+        exec(define_episode_enum, scope)
+        exec(define_enum_values, scope)
+        assert schema.get_type("Episode").values["EMPIRE"].value == 5
+
+        query = use_sdl.pop(0)
+        assert "graphql_sync" in query and "print(result)" in query
+        exec(query, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "Luke" in out and "appearsIn" in out and "EMPIRE" in out
+        assert out == expected_result(use_sdl)
+
+    def using_resolver_methods(capsys):
+        scope: Scope = {}
+        exec(resolvers, scope)
+        build_schema = get_snippets("usage/sdl")[0]
+        exec(build_schema, scope)
+
+        methods = get_snippets("usage/methods")
+        root_class = methods.pop(0)
+        assert root_class.startswith("class Root:")
+        assert "def human(self, info, id):" in root_class
+        exec(root_class, scope)
+        assert "Root" in scope
+
+        query = methods.pop(0)
+        assert "graphql_sync" in query and "Root()" in query
+        exec(query, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "R2-D2" in out and "primaryFunction" in out and "Astromech" in out
+        assert out == expected_result(methods)
+
+    def using_introspection(capsys):
+        introspect = get_snippets("usage/introspection")
+        get_query = introspect.pop(0)
+        assert "import get_introspection_query" in get_query
+        assert "descriptions=True" in get_query
+        scope: Scope = {}
+        exec(get_query, scope)
+        query = scope["query"]
+        assert query.lstrip().startswith("query IntrospectionQuery")
+        assert "description" in query
+        get_query = introspect.pop(0)
+        assert "descriptions=False" in get_query
+        scope2 = scope.copy()
+        exec(get_query, scope2)
+        query = scope2["query"]
+        assert query.lstrip().startswith("query IntrospectionQuery")
+        assert "description" not in query
+
+        exec(resolvers, scope)
+        create_schema = "\n".join(get_snippets("usage/schema")[1:])
+        exec(create_schema, scope)
+        get_result = introspect.pop(0)
+        assert "result = graphql_sync(" in get_result
+        exec(get_result, scope)
+        query_result = scope["introspection_query_result"]
+        assert query_result.errors is None
+        result = str(query_result.data)
+        result = "".join(result.split())
+        expected_result = introspect.pop(0)
+        result = "".join(result.split())
+        expected_result = "\n".join(expected_result.splitlines()[:7])
+        expected_result = "".join(expected_result.split())
+        assert result.startswith(expected_result)
+
+        build_schema = introspect.pop(0)
+        assert "schema = build_client_schema(" in build_schema
+        scope = {"introspection_query_result": query_result}
+        exec(build_schema, scope)
+        schema = scope["client_schema"]
+        assert list(schema.query_type.fields) == ["hero", "human", "droid"]
+        print_schema = introspect.pop(0)
+        scope = {"client_schema": schema}
+        assert "print_schema(" in print_schema
+        exec(print_schema, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "enum Episode {" in out
+        assert "id: String!" in out
+        assert "interface Character {" in out
+        assert "type Droid implements Character {" in out
+        assert "type Human implements Character {" in out
+        assert '"""A character in the Star Wars Trilogy"""' in out
+        assert '"""A humanoid creature in the Star Wars universe."""' in out
+
+    def parsing_graphql():
+        parser = get_snippets("usage/parser")
+
+        parse_document = parser.pop(0)
+        assert "document = parse(" in parse_document
+        scope: Scope = {}
+        exec(parse_document, scope)
+        document = scope["document"]
+        name = document.definitions[0].fields[0].name
+        assert name.value == "me"
+        assert str(name.loc) == "24:26"
+
+        parse_document2 = parser.pop(0)
+        assert "document = parse(" in parse_document2
+        assert "..., no_location=True" in parse_document2
+        parse_document = parse_document.replace('""")', '""", no_location=True)')
+        scope.clear()
+        exec(parse_document, scope)
+        document = scope["document"]
+        name = document.definitions[0].fields[0].name
+        assert name.value == "me"
+        assert name.loc is None
+
+        create_document = parser.pop(0)
+        assert "document = DocumentNode(" in create_document
+        assert "FieldDefinitionNode(" in create_document
+        assert "name=NameNode(value='me')," in create_document
+        scope = {}
+        exec(create_document, scope)
+        assert scope["document"] == document
+
+    def extending_a_schema(capsys):
+        scope: Scope = {}
+        exec(resolvers, scope)
+        create_schema = "\n".join(get_snippets("usage/schema")[1:])
+        exec(create_schema, scope)
+
+        extension = get_snippets("usage/extension")
+        extend_schema = extension.pop(0)
+        assert "extend_schema(" in extend_schema
+        exec(extend_schema, scope)
+        schema = scope["schema"]
+        human_type = schema.get_type("Human")
+        assert "lastName" in human_type.fields
+        attach_resolver = extension.pop(0)
+        exec(attach_resolver, scope)
+        assert human_type.fields["lastName"].resolve is scope["get_last_name"]
+
+        query = extension.pop(0)
+        assert "graphql_sync(" in query
+        exec(query, scope)
+        out, err = capsys.readouterr()
+        assert not err
+        assert "lastName" in out and "Skywalker" in out
+        assert out == expected_result(extension)
+
+    def validating_queries():
+        scope: Scope = {}
+        exec(resolvers, scope)
+        create_schema = "\n".join(get_snippets("usage/schema")[1:])
+        exec(create_schema, scope)
+
+        validator = get_snippets("usage/validator")
+        validate = validator.pop(0)
+        assert "errors = validate(" in validate
+        exec(validate, scope)
+        errors = str(scope["errors"])
+        assert errors == expected_errors(validator)
diff --git a/tests/test_star_wars_introspection.py b/tests/test_star_wars_introspection.py
new file mode 100644
index 0000000..a76d9cc
--- /dev/null
+++ b/tests/test_star_wars_introspection.py
@@ -0,0 +1,325 @@
+from graphql import graphql_sync
+
+from .star_wars_schema import star_wars_schema
+
+
+def query_star_wars(source):
+    result = graphql_sync(star_wars_schema, source)
+    assert result.errors is None
+    return result.data
+
+
+def describe_star_wars_introspection_tests():
+    def describe_basic_introspection():
+        def allows_querying_the_schema_for_types():
+            data = query_star_wars(
+                """
+                {
+                  __schema {
+                    types {
+                      name
+                    }
+                  }
+                }
+                """
+            )
+            # Include all types used by StarWars schema, introspection types and
+            # standard directives. For example, `Boolean` is used in `@skip`,
+            # `@include` and also inside introspection types.
+            assert data == {
+                "__schema": {
+                    "types": [
+                        {"name": "Human"},
+                        {"name": "Character"},
+                        {"name": "String"},
+                        {"name": "Episode"},
+                        {"name": "Droid"},
+                        {"name": "Query"},
+                        {"name": "Boolean"},
+                        {"name": "__Schema"},
+                        {"name": "__Type"},
+                        {"name": "__TypeKind"},
+                        {"name": "__Field"},
+                        {"name": "__InputValue"},
+                        {"name": "__EnumValue"},
+                        {"name": "__Directive"},
+                        {"name": "__DirectiveLocation"},
+                    ]
+                }
+            }
+
+        def allows_querying_the_schema_for_query_type():
+            data = query_star_wars(
+                """
+                {
+                  __schema {
+                    queryType {
+                      name
+                    }
+                  }
+                }
+                """
+            )
+
+            assert data == {"__schema": {"queryType": {"name": "Query"}}}
+
+        def allows_querying_the_schema_for_a_specific_type():
+            data = query_star_wars(
+                """
+                {
+                  __type(name: "Droid") {
+                    name
+                  }
+                }
+                """
+            )
+            assert data == {"__type": {"name": "Droid"}}
+
+        def allows_querying_the_schema_for_an_object_kind():
+            data = query_star_wars(
+                """
+                {
+                  __type(name: "Droid") {
+                    name
+                    kind
+                  }
+                }
+                """
+            )
+            assert data == {"__type": {"name": "Droid", "kind": "OBJECT"}}
+
+        def allows_querying_the_schema_for_an_interface_kind():
+            data = query_star_wars(
+                """
+                {
+                  __type(name: "Character") {
+                    name
+                    kind
+                  }
+                }
+                """
+            )
+            assert data == {"__type": {"name": "Character", "kind": "INTERFACE"}}
+
+        def allows_querying_the_schema_for_object_fields():
+            data = query_star_wars(
+                """
+                {
+                  __type(name: "Droid") {
+                    name
+                    fields {
+                      name
+                      type {
+                        name
+                        kind
+                      }
+                    }
+                  }
+                }
+                """
+            )
+            assert data == {
+                "__type": {
+                    "name": "Droid",
+                    "fields": [
+                        {"name": "id", "type": {"name": None, "kind": "NON_NULL"}},
+                        {"name": "name", "type": {"name": "String", "kind": "SCALAR"}},
+                        {"name": "friends", "type": {"name": None, "kind": "LIST"}},
+                        {"name": "appearsIn", "type": {"name": None, "kind": "LIST"}},
+                        {
+                            "name": "secretBackstory",
+                            "type": {"name": "String", "kind": "SCALAR"},
+                        },
+                        {
+                            "name": "primaryFunction",
+                            "type": {"name": "String", "kind": "SCALAR"},
+                        },
+                    ],
+                }
+            }
+
+        def allows_querying_the_schema_for_nested_object_fields():
+            data = query_star_wars(
+                """
+                {
+                  __type(name: "Droid") {
+                    name
+                    fields {
+                      name
+                      type {
+                        name
+                        kind
+                        ofType {
+                          name
+                          kind
+                        }
+                      }
+                    }
+                  }
+                }
+                """
+            )
+            assert data == {
+                "__type": {
+                    "name": "Droid",
+                    "fields": [
+                        {
+                            "name": "id",
+                            "type": {
+                                "name": None,
+                                "kind": "NON_NULL",
+                                "ofType": {"name": "String", "kind": "SCALAR"},
+                            },
+                        },
+                        {
+                            "name": "name",
+                            "type": {
+                                "name": "String",
+                                "kind": "SCALAR",
+                                "ofType": None,
+                            },
+                        },
+                        {
+                            "name": "friends",
+                            "type": {
+                                "name": None,
+                                "kind": "LIST",
+                                "ofType": {"name": "Character", "kind": "INTERFACE"},
+                            },
+                        },
+                        {
+                            "name": "appearsIn",
+                            "type": {
+                                "name": None,
+                                "kind": "LIST",
+                                "ofType": {"name": "Episode", "kind": "ENUM"},
+                            },
+                        },
+                        {
+                            "name": "secretBackstory",
+                            "type": {
+                                "name": "String",
+                                "kind": "SCALAR",
+                                "ofType": None,
+                            },
+                        },
+                        {
+                            "name": "primaryFunction",
+                            "type": {
+                                "name": "String",
+                                "kind": "SCALAR",
+                                "ofType": None,
+                            },
+                        },
+                    ],
+                }
+            }
+
+        def allows_querying_the_schema_for_field_args():
+            data = query_star_wars(
+                """
+                {
+                  __schema {
+                    queryType {
+                      fields {
+                        name
+                        args {
+                          name
+                          description
+                          type {
+                            name
+                            kind
+                            ofType {
+                              name
+                              kind
+                            }
+                          }
+                          defaultValue
+                        }
+                      }
+                    }
+                  }
+                }
+                """
+            )
+
+            assert data == {
+                "__schema": {
+                    "queryType": {
+                        "fields": [
+                            {
+                                "name": "hero",
+                                "args": [
+                                    {
+                                        "defaultValue": None,
+                                        "description": "If omitted, returns the hero of"
+                                        " the whole saga. If provided, returns the hero"
+                                        " of that particular episode.",
+                                        "name": "episode",
+                                        "type": {
+                                            "kind": "ENUM",
+                                            "name": "Episode",
+                                            "ofType": None,
+                                        },
+                                    }
+                                ],
+                            },
+                            {
+                                "name": "human",
+                                "args": [
+                                    {
+                                        "name": "id",
+                                        "description": "id of the human",
+                                        "type": {
+                                            "kind": "NON_NULL",
+                                            "name": None,
+                                            "ofType": {
+                                                "kind": "SCALAR",
+                                                "name": "String",
+                                            },
+                                        },
+                                        "defaultValue": None,
+                                    }
+                                ],
+                            },
+                            {
+                                "name": "droid",
+                                "args": [
+                                    {
+                                        "name": "id",
+                                        "description": "id of the droid",
+                                        "type": {
+                                            "kind": "NON_NULL",
+                                            "name": None,
+                                            "ofType": {
+                                                "kind": "SCALAR",
+                                                "name": "String",
+                                            },
+                                        },
+                                        "defaultValue": None,
+                                    }
+                                ],
+                            },
+                        ]
+                    }
+                }
+            }
+
+        def allows_querying_the_schema_for_documentation():
+            data = query_star_wars(
+                """
+                {
+                  __type(name: "Droid") {
+                    name
+                    description
+                  }
+                }
+                """
+            )
+
+            assert data == {
+                "__type": {
+                    "name": "Droid",
+                    "description": "A mechanical creature in the Star Wars universe.",
+                }
+            }
diff --git a/tests/test_star_wars_query.py b/tests/test_star_wars_query.py
new file mode 100644
index 0000000..9dd4644
--- /dev/null
+++ b/tests/test_star_wars_query.py
@@ -0,0 +1,397 @@
+from pytest import mark  # type: ignore
+
+from graphql import graphql, graphql_sync
+
+from .star_wars_schema import star_wars_schema as schema
+
+
+def describe_star_wars_query_tests():
+    def describe_basic_queries():
+        @mark.asyncio
+        async def correctly_identifies_r2_d2_as_hero_of_the_star_wars_saga():
+            source = """
+                query HeroNameQuery {
+                  hero {
+                    name
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == ({"hero": {"name": "R2-D2"}}, None)
+
+        @mark.asyncio
+        async def accepts_positional_arguments_to_graphql():
+            source = """
+                query HeroNameQuery {
+                  hero {
+                    name
+                  }
+                }
+                """
+            result = await graphql(schema, source)
+            assert result == ({"hero": {"name": "R2-D2"}}, None)
+
+            sync_result = graphql_sync(schema, source)
+            assert sync_result == result
+
+        @mark.asyncio
+        async def allows_us_to_query_for_the_id_and_friends_of_r2_d2():
+            source = """
+                query HeroNameAndFriendsQuery {
+                  hero {
+                    id
+                    name
+                    friends {
+                      name
+                    }
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {
+                    "hero": {
+                        "id": "2001",
+                        "name": "R2-D2",
+                        "friends": [
+                            {"name": "Luke Skywalker"},
+                            {"name": "Han Solo"},
+                            {"name": "Leia Organa"},
+                        ],
+                    }
+                },
+                None,
+            )
+
+    def describe_nested_queries():
+        @mark.asyncio
+        async def allows_us_to_query_for_the_friends_of_friends_of_r2_d2():
+            source = """
+                query NestedQuery {
+                  hero {
+                    name
+                    friends {
+                      name
+                      appearsIn
+                      friends {
+                        name
+                      }
+                    }
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {
+                    "hero": {
+                        "name": "R2-D2",
+                        "friends": [
+                            {
+                                "name": "Luke Skywalker",
+                                "appearsIn": ["NEW_HOPE", "EMPIRE", "JEDI"],
+                                "friends": [
+                                    {"name": "Han Solo"},
+                                    {"name": "Leia Organa"},
+                                    {"name": "C-3PO"},
+                                    {"name": "R2-D2"},
+                                ],
+                            },
+                            {
+                                "name": "Han Solo",
+                                "appearsIn": ["NEW_HOPE", "EMPIRE", "JEDI"],
+                                "friends": [
+                                    {"name": "Luke Skywalker"},
+                                    {"name": "Leia Organa"},
+                                    {"name": "R2-D2"},
+                                ],
+                            },
+                            {
+                                "name": "Leia Organa",
+                                "appearsIn": ["NEW_HOPE", "EMPIRE", "JEDI"],
+                                "friends": [
+                                    {"name": "Luke Skywalker"},
+                                    {"name": "Han Solo"},
+                                    {"name": "C-3PO"},
+                                    {"name": "R2-D2"},
+                                ],
+                            },
+                        ],
+                    }
+                },
+                None,
+            )
+
+    def describe_using_ids_and_query_parameters_to_refetch_objects():
+        @mark.asyncio
+        async def allows_us_to_query_for_r2_d2_directly_using_his_id():
+            source = """
+                query {
+                  droid(id: "2001") {
+                    name
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == ({"droid": {"name": "R2-D2"}}, None)
+
+        @mark.asyncio
+        async def allows_us_to_query_characters_directly_using_their_id():
+            source = """
+                query FetchLukeAndC3POQuery {
+                  human(id: "1000") {
+                    name
+                  }
+                  droid(id: "2000") {
+                    name
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {"human": {"name": "Luke Skywalker"}, "droid": {"name": "C-3PO"}},
+                None,
+            )
+
+        @mark.asyncio
+        async def allows_creating_a_generic_query_to_fetch_luke_using_his_id():
+            source = """
+                query FetchSomeIDQuery($someId: String!) {
+                  human(id: $someId) {
+                    name
+                  }
+                }
+                """
+            variable_values = {"someId": "1000"}
+            result = await graphql(
+                schema=schema, source=source, variable_values=variable_values
+            )
+            assert result == ({"human": {"name": "Luke Skywalker"}}, None)
+
+        @mark.asyncio
+        async def allows_creating_a_generic_query_to_fetch_han_using_his_id():
+            source = """
+                query FetchSomeIDQuery($someId: String!) {
+                  human(id: $someId) {
+                    name
+                  }
+                }
+                """
+            variable_values = {"someId": "1002"}
+            result = await graphql(
+                schema=schema, source=source, variable_values=variable_values
+            )
+            assert result == ({"human": {"name": "Han Solo"}}, None)
+
+        @mark.asyncio
+        async def generic_query_that_gets_null_back_when_passed_invalid_id():
+            source = """
+                query humanQuery($id: String!) {
+                  human(id: $id) {
+                    name
+                  }
+                }
+                """
+            variable_values = {"id": "not a valid id"}
+            result = await graphql(
+                schema=schema, source=source, variable_values=variable_values
+            )
+            assert result == ({"human": None}, None)
+
+    def describe_using_aliases_to_change_the_key_in_the_response():
+        @mark.asyncio
+        async def allows_us_to_query_for_luke_changing_his_key_with_an_alias():
+            source = """
+                query FetchLukeAliased {
+                  luke: human(id: "1000") {
+                    name
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == ({"luke": {"name": "Luke Skywalker"}}, None)
+
+        @mark.asyncio
+        async def query_for_luke_and_leia_using_two_root_fields_and_an_alias():
+            source = """
+                query FetchLukeAndLeiaAliased {
+                  luke: human(id: "1000") {
+                    name
+                  }
+                  leia: human(id: "1003") {
+                    name
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {"luke": {"name": "Luke Skywalker"}, "leia": {"name": "Leia Organa"}},
+                None,
+            )
+
+    def describe_uses_fragments_to_express_more_complex_queries():
+        @mark.asyncio
+        async def allows_us_to_query_using_duplicated_content():
+            source = """
+                query DuplicateFields {
+                  luke: human(id: "1000") {
+                    name
+                    homePlanet
+                  }
+                  leia: human(id: "1003") {
+                    name
+                    homePlanet
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {
+                    "luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"},
+                    "leia": {"name": "Leia Organa", "homePlanet": "Alderaan"},
+                },
+                None,
+            )
+
+        @mark.asyncio
+        async def allows_us_to_use_a_fragment_to_avoid_duplicating_content():
+            source = """
+                query UseFragment {
+                  luke: human(id: "1000") {
+                    ...HumanFragment
+                  }
+                  leia: human(id: "1003") {
+                    ...HumanFragment
+                  }
+                }
+                fragment HumanFragment on Human {
+                  name
+                  homePlanet
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {
+                    "luke": {"name": "Luke Skywalker", "homePlanet": "Tatooine"},
+                    "leia": {"name": "Leia Organa", "homePlanet": "Alderaan"},
+                },
+                None,
+            )
+
+    def describe_using_typename_to_find_the_type_of_an_object():
+        @mark.asyncio
+        async def allows_us_to_verify_that_r2_d2_is_a_droid():
+            source = """
+                query CheckTypeOfR2 {
+                  hero {
+                    __typename
+                    name
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == ({"hero": {"__typename": "Droid", "name": "R2-D2"}}, None)
+
+        @mark.asyncio
+        async def allows_us_to_verify_that_luke_is_a_human():
+            source = """
+                query CheckTypeOfLuke {
+                  hero(episode: EMPIRE) {
+                    __typename
+                    name
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {"hero": {"__typename": "Human", "name": "Luke Skywalker"}},
+                None,
+            )
+
+    def describe_reporting_errors_raised_in_resolvers():
+        @mark.asyncio
+        async def correctly_reports_error_on_accessing_secret_backstory():
+            source = """
+                query HeroNameQuery {
+                  hero {
+                    name
+                    secretBackstory
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {"hero": {"name": "R2-D2", "secretBackstory": None}},
+                [
+                    {
+                        "message": "secretBackstory is secret.",
+                        "locations": [(5, 21)],
+                        "path": ["hero", "secretBackstory"],
+                    }
+                ],
+            )
+
+        @mark.asyncio
+        async def correctly_reports_error_on_accessing_backstory_in_a_list():
+            source = """
+                query HeroNameQuery {
+                  hero {
+                    name
+                    friends {
+                      name
+                      secretBackstory
+                    }
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {
+                    "hero": {
+                        "name": "R2-D2",
+                        "friends": [
+                            {"name": "Luke Skywalker", "secretBackstory": None},
+                            {"name": "Han Solo", "secretBackstory": None},
+                            {"name": "Leia Organa", "secretBackstory": None},
+                        ],
+                    }
+                },
+                [
+                    {
+                        "message": "secretBackstory is secret.",
+                        "locations": [(7, 23)],
+                        "path": ["hero", "friends", 0, "secretBackstory"],
+                    },
+                    {
+                        "message": "secretBackstory is secret.",
+                        "locations": [(7, 23)],
+                        "path": ["hero", "friends", 1, "secretBackstory"],
+                    },
+                    {
+                        "message": "secretBackstory is secret.",
+                        "locations": [(7, 23)],
+                        "path": ["hero", "friends", 2, "secretBackstory"],
+                    },
+                ],
+            )
+
+        @mark.asyncio
+        async def correctly_reports_error_on_accessing_through_an_alias():
+            source = """
+                query HeroNameQuery {
+                  mainHero: hero {
+                    name
+                    story: secretBackstory
+                  }
+                }
+                """
+            result = await graphql(schema=schema, source=source)
+            assert result == (
+                {"mainHero": {"name": "R2-D2", "story": None}},
+                [
+                    {
+                        "message": "secretBackstory is secret.",
+                        "locations": [(5, 21)],
+                        "path": ["mainHero", "story"],
+                    }
+                ],
+            )
diff --git a/tests/test_star_wars_validation.py b/tests/test_star_wars_validation.py
new file mode 100644
index 0000000..5ec26f1
--- /dev/null
+++ b/tests/test_star_wars_validation.py
@@ -0,0 +1,106 @@
+from graphql.language import parse, Source
+from graphql.validation import validate
+
+from .star_wars_schema import star_wars_schema
+
+
+def validation_errors(query):
+    """Helper function to test a query and the expected response."""
+    source = Source(query, "StarWars.graphql")
+    ast = parse(source)
+    return validate(star_wars_schema, ast)
+
+
+def describe_star_wars_validation_tests():
+    def describe_basic_queries():
+        def validates_a_complex_but_valid_query():
+            query = """
+                query NestedQueryWithFragment {
+                  hero {
+                    ...NameAndAppearances
+                    friends {
+                      ...NameAndAppearances
+                      friends {
+                        ...NameAndAppearances
+                      }
+                    }
+                  }
+                }
+
+                fragment NameAndAppearances on Character {
+                  name
+                  appearsIn
+                }
+                """
+            assert not validation_errors(query)
+
+        def notes_that_non_existent_fields_are_invalid():
+            query = """
+                query HeroSpaceshipQuery {
+                  hero {
+                    favoriteSpaceship
+                  }
+                }
+                """
+            assert validation_errors(query)
+
+        def requires_fields_on_object():
+            query = """
+                query HeroNoFieldsQuery {
+                  hero
+                }
+                """
+            assert validation_errors(query)
+
+        def disallows_fields_on_scalars():
+            query = """
+                query HeroFieldsOnScalarQuery {
+                  hero {
+                    name {
+                      firstCharacterOfName
+                    }
+                  }
+                }
+                """
+            assert validation_errors(query)
+
+        def disallows_object_fields_on_interfaces():
+            query = """
+                query DroidFieldOnCharacter {
+                  hero {
+                    name
+                    primaryFunction
+                  }
+                }
+                """
+            assert validation_errors(query)
+
+        def allows_object_fields_in_fragments():
+            query = """
+                query DroidFieldInFragment {
+                  hero {
+                    name
+                    ...DroidFields
+                  }
+                }
+
+                fragment DroidFields on Droid {
+                  primaryFunction
+                }
+                """
+            assert not validation_errors(query)
+
+        def allows_object_fields_in_inline_fragments():
+            query = """
+                query DroidFieldInFragment {
+                  hero {
+                    name
+                    ...DroidFields
+                  }
+                }
+
+                fragment DroidFields on Droid {
+                    primaryFunction
+                }
+                """
+            assert not validation_errors(query)
diff --git a/tests/test_user_registry.py b/tests/test_user_registry.py
new file mode 100644
index 0000000..5e8e287
--- /dev/null
+++ b/tests/test_user_registry.py
@@ -0,0 +1,586 @@
+"""User registry demo.
+
+This is an additional end-to-end test and demo for running the basic GraphQL
+operations on a simulated user registry database backend.
+"""
+
+from asyncio import sleep, wait
+from enum import Enum
+from typing import Any, Dict, List, NamedTuple, Optional
+
+from pytest import fixture, mark  # type: ignore
+
+from graphql import (
+    graphql,
+    parse,
+    subscribe,
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLEnumType,
+    GraphQLField,
+    GraphQLID,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+
+from graphql.pyutils import EventEmitter, EventEmitterAsyncIterator
+from graphql.subscription.map_async_iterator import MapAsyncIterator
+
+
+class User(NamedTuple):
+    """A simple user object class."""
+
+    firstName: str
+    lastName: str
+    tweets: Optional[int]
+    id: Optional[str] = None
+    verified: bool = False
+
+
+class MutationEnum(Enum):
+    """Mutation event type"""
+
+    CREATED = "created"
+    UPDATED = "updated"
+    DELETED = "deleted"
+
+
+class UserRegistry:
+    """"Simulation of a user registry with asynchronous database backend access."""
+
+    def __init__(self, **users):
+        self._registry: Dict[str, User] = users
+        self._emitter = EventEmitter()
+
+    async def get(self, id_: str) -> Optional[User]:
+        """Get a user object from the registry"""
+        await sleep(0)
+        return self._registry.get(id_)
+
+    async def create(self, **kwargs) -> User:
+        """Get a user object in the registry"""
+        await sleep(0)
+        id_ = str(len(self._registry))
+        user = User(id=id_, **kwargs)
+        self._registry[id_] = user
+        self.emit_event(MutationEnum.CREATED, user)
+        return user
+
+    async def update(self, id_: str, **kwargs) -> User:
+        """Update a user object in the registry"""
+        await sleep(0)
+        # noinspection PyProtectedMember
+        user = self._registry[id_]._replace(**kwargs)
+        self._registry[id_] = user
+        self.emit_event(MutationEnum.UPDATED, user)
+        return user
+
+    async def delete(self, id_: str) -> User:
+        """Update a user object in the registry"""
+        await sleep(0)
+        user = self._registry.pop(id_)
+        self.emit_event(MutationEnum.DELETED, user)
+        return user
+
+    def emit_event(self, mutation: MutationEnum, user: User) -> None:
+        """Emit mutation events for the given object and its class"""
+        emit = self._emitter.emit
+        payload = {"user": user, "mutation": mutation.value}
+        emit("User", payload)  # notify all user subscriptions
+        emit(f"User_{user.id}", payload)  # notify single user subscriptions
+
+    def event_iterator(self, id_: str) -> EventEmitterAsyncIterator:
+        event_name = "User" if id_ is None else f"User_{id_}"
+        return EventEmitterAsyncIterator(self._emitter, event_name)
+
+
+mutation_type = GraphQLEnumType("MutationType", MutationEnum)
+
+user_type = GraphQLObjectType(
+    "UserType",
+    {
+        "id": GraphQLField(GraphQLNonNull(GraphQLID)),
+        "firstName": GraphQLField(GraphQLNonNull(GraphQLString)),
+        "lastName": GraphQLField(GraphQLNonNull(GraphQLString)),
+        "tweets": GraphQLField(GraphQLInt),
+        "verified": GraphQLField(GraphQLNonNull(GraphQLBoolean)),
+    },
+)
+
+user_input_type = GraphQLInputObjectType(
+    "UserInputType",
+    {
+        "firstName": GraphQLInputField(GraphQLNonNull(GraphQLString)),
+        "lastName": GraphQLInputField(GraphQLNonNull(GraphQLString)),
+        "tweets": GraphQLInputField(GraphQLInt),
+        "verified": GraphQLInputField(GraphQLBoolean),
+    },
+)
+
+subscription_user_type = GraphQLObjectType(
+    "SubscriptionUserType",
+    {"mutation": GraphQLField(mutation_type), "user": GraphQLField(user_type)},
+)
+
+
+async def resolve_user(_root, info, **args):
+    """Resolver function for fetching a user object"""
+    return await info.context["registry"].get(args["id"])
+
+
+async def resolve_create_user(_root, info, data):
+    """Resolver function for creating a user object"""
+    user = await info.context["registry"].create(**data)
+    return user
+
+
+# noinspection PyShadowingBuiltins
+async def resolve_update_user(_root, info, id, data):
+    """Resolver function for updating a user object"""
+    user = await info.context["registry"].update(id, **data)
+    return user
+
+
+# noinspection PyShadowingBuiltins
+async def resolve_delete_user(_root, info, id):
+    """Resolver function for deleting a user object"""
+    user = await info.context["registry"].get(id)
+    await info.context["registry"].delete(user.id)
+    return True
+
+
+# noinspection PyShadowingBuiltins
+async def subscribe_user(_root, info, id=None):
+    """Subscribe to mutations of a specific user object or all user objects"""
+    async_iterator = info.context["registry"].event_iterator(id)
+    async for msg in async_iterator:
+        yield msg
+
+
+# noinspection PyShadowingBuiltins,PyUnusedLocal
+async def resolve_subscription_user(event, info, id):
+    """Resolver function for user subscriptions"""
+    user = event["user"]
+    mutation = MutationEnum(event["mutation"]).value
+    return {"user": user, "mutation": mutation}
+
+
+schema = GraphQLSchema(
+    query=GraphQLObjectType(
+        "RootQueryType",
+        {
+            "User": GraphQLField(
+                user_type, args={"id": GraphQLArgument(GraphQLID)}, resolve=resolve_user
+            )
+        },
+    ),
+    mutation=GraphQLObjectType(
+        "RootMutationType",
+        {
+            "createUser": GraphQLField(
+                user_type,
+                args={"data": GraphQLArgument(GraphQLNonNull(user_input_type))},
+                resolve=resolve_create_user,
+            ),
+            "deleteUser": GraphQLField(
+                GraphQLBoolean,
+                args={"id": GraphQLArgument(GraphQLNonNull(GraphQLID))},
+                resolve=resolve_delete_user,
+            ),
+            "updateUser": GraphQLField(
+                user_type,
+                args={
+                    "id": GraphQLArgument(GraphQLNonNull(GraphQLID)),
+                    "data": GraphQLArgument(GraphQLNonNull(user_input_type)),
+                },
+                resolve=resolve_update_user,
+            ),
+        },
+    ),
+    subscription=GraphQLObjectType(
+        "RootSubscriptionType",
+        {
+            "subscribeUser": GraphQLField(
+                subscription_user_type,
+                args={"id": GraphQLArgument(GraphQLID)},
+                subscribe=subscribe_user,
+                resolve=resolve_subscription_user,
+            )
+        },
+    ),
+)
+
+
+@fixture
+def context():
+    return {"registry": UserRegistry()}
+
+
+def describe_query():
+    @mark.asyncio
+    async def query_user(context):
+        user = await context["registry"].create(
+            firstName="John", lastName="Doe", tweets=42, verified=True
+        )
+
+        query = """
+            query ($userId: ID!) {
+                User(id: $userId) {
+                    id, firstName, lastName, tweets, verified
+                }
+            }
+            """
+
+        variables = {"userId": user.id}
+        result = await graphql(
+            schema, query, context_value=context, variable_values=variables
+        )
+
+        assert not result.errors
+        assert result.data == {
+            "User": {
+                "id": user.id,
+                "firstName": user.firstName,
+                "lastName": user.lastName,
+                "tweets": user.tweets,
+                "verified": user.verified,
+            }
+        }
+
+
+def describe_mutation():
+    @mark.asyncio
+    async def create_user(context):
+        received = {}
+
+        def receiver(event_name):
+            def receive(msg):
+                received[event_name] = msg
+
+            return receive
+
+        # noinspection PyProtectedMember
+        add_listener = context["registry"]._emitter.add_listener
+        add_listener("User", receiver("User"))
+        add_listener("User_0", receiver("User_0"))
+
+        query = """
+            mutation ($userData: UserInputType!) {
+                createUser(data: $userData) {
+                    id, firstName, lastName, tweets, verified
+                }
+            }
+            """
+        user_data = dict(firstName="John", lastName="Doe", tweets=42, verified=True)
+        variables = {"userData": user_data}
+        result = await graphql(
+            schema, query, context_value=context, variable_values=variables
+        )
+
+        user = await context["registry"].get("0")
+        assert user == User(id="0", **user_data)  # type: ignore
+
+        assert result.errors is None
+        assert result.data == {
+            "createUser": {
+                "id": user.id,
+                "firstName": user.firstName,
+                "lastName": user.lastName,
+                "tweets": user.tweets,
+                "verified": user.verified,
+            }
+        }
+
+        assert received == {
+            "User": {"user": user, "mutation": MutationEnum.CREATED.value},
+            "User_0": {"user": user, "mutation": MutationEnum.CREATED.value},
+        }
+
+    @mark.asyncio
+    async def update_user(context):
+        received = {}
+
+        def receiver(event_name):
+            def receive(msg):
+                received[event_name] = msg
+
+            return receive
+
+        # noinspection PyProtectedMember
+        add_listener = context["registry"]._emitter.add_listener
+        add_listener("User", receiver("User"))
+        add_listener("User_0", receiver("User_0"))
+
+        user = await context["registry"].create(
+            firstName="John", lastName="Doe", tweets=42, verified=True
+        )
+        user_data = {
+            "firstName": "Jane",
+            "lastName": "Roe",
+            "tweets": 210,
+            "verified": False,
+        }
+
+        query = """
+            mutation ($userId: ID!, $userData: UserInputType!) {
+                updateUser(id: $userId, data: $userData) {
+                    id, firstName, lastName, tweets, verified
+                }
+            }"""
+
+        variables = {"userId": user.id, "userData": user_data}
+        result = await graphql(
+            schema, query, context_value=context, variable_values=variables
+        )
+
+        user = await context["registry"].get("0")
+        assert user == User(id="0", **user_data)  # type: ignore
+
+        assert result.errors is None
+        assert result.data == {
+            "updateUser": {
+                "id": user.id,
+                "firstName": user.firstName,
+                "lastName": user.lastName,
+                "tweets": user.tweets,
+                "verified": user.verified,
+            }
+        }
+
+        assert received == {
+            "User": {"user": user, "mutation": MutationEnum.UPDATED.value},
+            "User_0": {"user": user, "mutation": MutationEnum.UPDATED.value},
+        }
+
+    @mark.asyncio
+    async def delete_user(context):
+        received = {}
+
+        def receiver(name):
+            def receive(msg):
+                received[name] = msg
+
+            return receive
+
+        # noinspection PyProtectedMember
+        add_listener = context["registry"]._emitter.add_listener
+        add_listener("User", receiver("User"))
+        add_listener("User_0", receiver("User_0"))
+
+        user = await context["registry"].create(
+            firstName="John", lastName="Doe", tweets=42, verified=True
+        )
+
+        query = """
+            mutation ($userId: ID!) {
+                deleteUser(id: $userId)
+            }
+            """
+
+        variables = {"userId": user.id}
+        result = await graphql(
+            schema, query, context_value=context, variable_values=variables
+        )
+
+        assert result.errors is None
+        assert result.data == {"deleteUser": True}
+
+        assert await context["registry"].get(user.id) is None
+
+        assert received == {
+            "User": {"user": user, "mutation": MutationEnum.DELETED.value},
+            "User_0": {"user": user, "mutation": MutationEnum.DELETED.value},
+        }
+
+
+def describe_subscription():
+    @mark.asyncio
+    async def subscribe_to_user_mutations(context):
+        query = """
+            subscription ($userId: ID!) {
+                subscribeUser(id: $userId) {
+                    mutation
+                    user { id, firstName, lastName, tweets, verified }
+                }
+            }
+            """
+
+        variables = {"userId": "0"}
+        subscription_one = await subscribe(
+            schema, parse(query), context_value=context, variable_values=variables
+        )
+
+        assert isinstance(subscription_one, MapAsyncIterator)
+
+        query = """
+            subscription {
+                subscribeUser(id: null) {
+                    mutation
+                    user { id, firstName, lastName, tweets, verified }
+                }
+            }
+            """
+
+        subscription_all = await subscribe(schema, parse(query), context_value=context)
+
+        assert isinstance(subscription_all, MapAsyncIterator)
+
+        received_one = []
+        received_all = []
+
+        async def mutate_users():
+            await sleep(0)  # make sure receivers are running
+            await graphql(
+                schema,
+                """
+                mutation {createUser(data: {
+                    firstName: "John"
+                    lastName: "Doe"
+                    tweets: 42
+                    verified: true}) { id }
+                }""",
+                context_value=context,
+            )
+            await graphql(
+                schema,
+                """
+                mutation {createUser(data: {
+                    firstName: "James"
+                    lastName: "Doe"
+                    tweets: 4
+                    verified: false}) { id }
+                }""",
+                context_value=context,
+            )
+            await graphql(
+                schema,
+                """
+                mutation {updateUser(id: 0, data: {
+                    firstName: "Jane"
+                    lastName: "Roe"
+                    tweets: 210
+                    verified: false}) { id }
+                }""",
+                context_value=context,
+            )
+            await graphql(
+                schema,
+                """
+                mutation {updateUser(id: 1, data: {
+                    firstName: "Janette"
+                    lastName: "Roe"
+                    tweets: 20
+                    verified: true}) { id }
+                }""",
+                context_value=context,
+            )
+            await graphql(
+                schema,
+                """
+                mutation {deleteUser(id: "0")}
+                """,
+                context_value=context,
+            )
+            await graphql(
+                schema,
+                """
+                mutation {deleteUser(id: "1")}
+                """,
+                context_value=context,
+            )
+
+        async def receive_one():
+            async for result in subscription_one:  # type: ignore
+                received_one.append(result)
+                if len(received_one) == 3:
+                    break
+
+        async def receive_all():
+            async for result in subscription_all:  # type: ignore
+                received_all.append(result)
+                if len(received_all) == 6:
+                    break
+
+        done, pending = await wait(
+            [mutate_users(), receive_one(), receive_all()], timeout=1
+        )
+        assert not pending
+
+        expected_data: List[Dict[str, Any]] = [
+            {
+                "mutation": "CREATED",
+                "user": {
+                    "id": "0",
+                    "firstName": "John",
+                    "lastName": "Doe",
+                    "tweets": 42,
+                    "verified": True,
+                },
+            },
+            {
+                "mutation": "CREATED",
+                "user": {
+                    "id": "1",
+                    "firstName": "James",
+                    "lastName": "Doe",
+                    "tweets": 4,
+                    "verified": False,
+                },
+            },
+            {
+                "mutation": "UPDATED",
+                "user": {
+                    "id": "0",
+                    "firstName": "Jane",
+                    "lastName": "Roe",
+                    "tweets": 210,
+                    "verified": False,
+                },
+            },
+            {
+                "mutation": "UPDATED",
+                "user": {
+                    "id": "1",
+                    "firstName": "Janette",
+                    "lastName": "Roe",
+                    "tweets": 20,
+                    "verified": True,
+                },
+            },
+            {
+                "mutation": "DELETED",
+                "user": {
+                    "id": "0",
+                    "firstName": "Jane",
+                    "lastName": "Roe",
+                    "tweets": 210,
+                    "verified": False,
+                },
+            },
+            {
+                "mutation": "DELETED",
+                "user": {
+                    "id": "1",
+                    "firstName": "Janette",
+                    "lastName": "Roe",
+                    "tweets": 20,
+                    "verified": True,
+                },
+            },
+        ]
+
+        assert received_one == [
+            ({"subscribeUser": data}, None)
+            for data in expected_data
+            if data["user"]["id"] == "0"
+        ]
+        assert received_all == [
+            ({"subscribeUser": data}, None) for data in expected_data
+        ]
+
+        await sleep(0)
diff --git a/tests/test_version.py b/tests/test_version.py
new file mode 100644
index 0000000..a7a1b4e
--- /dev/null
+++ b/tests/test_version.py
@@ -0,0 +1,107 @@
+import re
+
+import graphql
+from graphql.version import (
+    VersionInfo,
+    version,
+    version_info,
+    version_js,
+    version_info_js,
+)
+
+_re_version = re.compile(r"(\d+)\.(\d+)\.(\d+)(?:(a|b|c)(\d+))?$")
+
+
+def describe_version():
+    def describe_version_info_class():
+        def create_version_info_from_fields():
+            v = VersionInfo(1, 2, 3, "alpha", 4)
+            assert v.major == 1
+            assert v.minor == 2
+            assert v.micro == 3
+            assert v.releaselevel == "alpha"
+            assert v.serial == 4
+
+        def create_version_info_from_str():
+            v = VersionInfo.from_str("1.2.3")
+            assert v.major == 1
+            assert v.minor == 2
+            assert v.micro == 3
+            assert v.releaselevel == "final"
+            assert v.serial == 0
+            v = VersionInfo.from_str("1.2.3a4")
+            assert v.major == 1
+            assert v.minor == 2
+            assert v.micro == 3
+            assert v.releaselevel == "alpha"
+            assert v.serial == 4
+            v = VersionInfo.from_str("1.2.3beta4")
+            assert v.major == 1
+            assert v.minor == 2
+            assert v.micro == 3
+            assert v.releaselevel == "beta"
+            assert v.serial == 4
+            v = VersionInfo.from_str("12.34.56rc789")
+            assert v.major == 12
+            assert v.minor == 34
+            assert v.micro == 56
+            assert v.releaselevel == "candidate"
+            assert v.serial == 789
+
+        def serialize_as_str():
+            v = VersionInfo(1, 2, 3, "final", 0)
+            assert str(v) == "1.2.3"
+            v = VersionInfo(1, 2, 3, "alpha", 4)
+            assert str(v) == "1.2.3a4"
+
+    def describe_graphql_core_version():
+        def base_package_has_correct_version():
+            assert graphql.__version__ == version
+            assert graphql.version == version
+
+        def base_package_has_correct_version_info():
+            assert graphql.__version_info__ is version_info
+            assert graphql.version_info is version_info
+
+        def version_has_correct_format():
+            assert isinstance(version, str)
+            assert _re_version.match(version)
+
+        def version_info_has_correct_fields():
+            assert isinstance(version_info, tuple)
+            assert str(version_info) == version
+            groups = _re_version.match(version).groups()  # type: ignore
+            assert version_info.major == int(groups[0])
+            assert version_info.minor == int(groups[1])
+            assert version_info.micro == int(groups[2])
+            if groups[3] is None:  # pragma: no cover
+                assert groups[4] is None
+            else:  # pragma: no cover
+                assert version_info.releaselevel[:1] == groups[3]
+                assert version_info.serial == int(groups[4])
+
+    def describe_graphql_js_version():
+        def base_package_has_correct_version_js():
+            assert graphql.__version_js__ == version_js
+            assert graphql.version_js == version_js
+
+        def base_package_has_correct_version_info_js():
+            assert graphql.__version_info_js__ is version_info_js
+            assert graphql.version_info_js is version_info_js
+
+        def version_js_has_correct_format():
+            assert isinstance(version_js, str)
+            assert _re_version.match(version_js)
+
+        def version_info_js_has_correct_fields():
+            assert isinstance(version_info_js, tuple)
+            assert str(version_info_js) == version_js
+            groups = _re_version.match(version_js).groups()  # type: ignore
+            assert version_info_js.major == int(groups[0])
+            assert version_info_js.minor == int(groups[1])
+            assert version_info_js.micro == int(groups[2])
+            if groups[3] is None:  # pragma: no cover
+                assert groups[4] is None
+            else:  # pragma: no cover
+                assert version_info_js.releaselevel[:1] == groups[3]
+                assert version_info_js.serial == int(groups[4])
diff --git a/tests/type/__init__.py b/tests/type/__init__.py
new file mode 100644
index 0000000..aaa6fa2
--- /dev/null
+++ b/tests/type/__init__.py
@@ -0,0 +1 @@
+"""Tests for graphql.type"""
diff --git a/tests/type/test_custom_scalars.py b/tests/type/test_custom_scalars.py
new file mode 100644
index 0000000..1cfe892
--- /dev/null
+++ b/tests/type/test_custom_scalars.py
@@ -0,0 +1,195 @@
+from typing import Any, Dict, NamedTuple
+
+from graphql import graphql_sync
+from graphql.error import GraphQLError
+from graphql.language import ValueNode
+from graphql.pyutils import inspect, is_finite
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLField,
+    GraphQLFloat,
+    GraphQLObjectType,
+    GraphQLScalarType,
+    GraphQLSchema,
+)
+from graphql.utilities import value_from_ast_untyped
+
+# this test is not (yet) part of GraphQL.js, see
+# https://github.com/graphql/graphql-js/issues/2657
+
+
+class Money(NamedTuple):
+    amount: float
+    currency: str
+
+
+def serialize_money(output_value: Any) -> Dict[str, float]:
+    if not isinstance(output_value, Money):
+        raise GraphQLError("Cannot serialize money value: " + inspect(output_value))
+    return output_value._asdict()
+
+
+def parse_money_value(input_value: Any) -> Money:
+    if not isinstance(input_value, Money):
+        raise GraphQLError("Cannot parse money value: " + inspect(input_value))
+    return input_value
+
+
+def parse_money_literal(value_node: ValueNode, variables=None) -> Money:
+    money = value_from_ast_untyped(value_node, variables)
+    if variables is not None and (
+        # variables are not set when checked with ValuesIOfCorrectTypeRule
+        not money
+        or not is_finite(money.get("amount"))
+        or not isinstance(money.get("currency"), str)
+    ):
+        raise GraphQLError("Cannot parse literal money value: " + inspect(money))
+    return Money(**money)
+
+
+MoneyScalar = GraphQLScalarType(
+    name="Money",
+    serialize=serialize_money,
+    parse_value=parse_money_value,
+    parse_literal=parse_money_literal,
+)
+
+
+def resolve_balance(root, _info):
+    return root
+
+
+def resolve_to_euros(_root, _info, money):
+    amount = money.amount
+    currency = money.currency
+    if not amount or currency == "EUR":
+        return amount
+    if currency == "DM":
+        return amount * 0.5
+    raise ValueError("Cannot convert to euros: " + inspect(money))
+
+
+schema = GraphQLSchema(
+    query=GraphQLObjectType(
+        name="RootQueryType",
+        fields={
+            "balance": GraphQLField(MoneyScalar, resolve=resolve_balance),
+            "toEuros": GraphQLField(
+                GraphQLFloat,
+                args={"money": GraphQLArgument(MoneyScalar)},
+                resolve=resolve_to_euros,
+            ),
+        },
+    )
+)
+
+
+def describe_custom_scalar():
+    def serialize():
+        source = """
+            {
+              balance
+            }
+            """
+
+        result = graphql_sync(schema, source, root_value=Money(42, "DM"))
+        assert result == ({"balance": {"amount": 42, "currency": "DM"}}, None)
+
+    def serialize_with_error():
+        source = """
+            {
+              balance
+            }
+            """
+
+        result = graphql_sync(schema, source, root_value=21)
+        assert result == (
+            {"balance": None},
+            [
+                {
+                    "message": "Cannot serialize money value: 21",
+                    "locations": [(3, 15)],
+                    "path": ["balance"],
+                }
+            ],
+        )
+
+    def parse_value():
+        source = """
+            query Money($money: Money!) {
+              toEuros(money: $money)
+            }
+            """
+
+        result = graphql_sync(
+            schema, source, variable_values={"money": Money(24, "EUR")}
+        )
+        assert result == ({"toEuros": 24}, None)
+
+        result = graphql_sync(
+            schema, source, variable_values={"money": Money(42, "DM")}
+        )
+        assert result == ({"toEuros": 21}, None)
+
+    def parse_value_with_error():
+        source = """
+            query Money($money: Money!) {
+              toEuros(money: $money)
+            }
+            """
+
+        result = graphql_sync(
+            schema, source, variable_values={"money": Money(42, "USD")}
+        )
+        assert result == (
+            {"toEuros": None},
+            [
+                {
+                    "message": "Cannot convert to euros: (42, 'USD')",
+                    "locations": [(3, 15)],
+                }
+            ],
+        )
+
+        result = graphql_sync(schema, source, variable_values={"money": 21})
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Variable '$money' got invalid value 21;"
+                    " Cannot parse money value: 21",
+                    "locations": [(2, 25)],
+                }
+            ],
+        )
+
+    def parse_literal():
+        source = """
+            query Money($amount: Float!, $currency: String!) {
+              toEuros(money: {amount: $amount, currency: $currency})
+            }
+            """
+
+        variable_values = {"amount": 42, "currency": "DM"}
+        result = graphql_sync(schema, source, variable_values=variable_values)
+        assert result == ({"toEuros": 21}, None)
+
+    def parse_literal_with_errors():
+        source = """
+            query Money($amount: String!, $currency: Float!) {
+              toEuros(money: {amount: $amount, currency: $currency})
+            }
+            """
+
+        variable_values = {"amount": "DM", "currency": 42}
+        result = graphql_sync(schema, source, variable_values=variable_values)
+        assert result == (
+            {"toEuros": None},
+            [
+                {
+                    "message": "Argument 'money' has invalid value"
+                    " {amount: $amount, currency: $currency}.",
+                    "locations": [(3, 30)],
+                },
+            ],
+        )
diff --git a/tests/type/test_definition.py b/tests/type/test_definition.py
new file mode 100644
index 0000000..c0c784a
--- /dev/null
+++ b/tests/type/test_definition.py
@@ -0,0 +1,1807 @@
+from math import isnan, nan
+from typing import cast
+
+from pytest import mark, raises  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.language import (
+    parse_value,
+    EnumTypeDefinitionNode,
+    EnumTypeExtensionNode,
+    EnumValueNode,
+    Node,
+    InputObjectTypeDefinitionNode,
+    InputObjectTypeExtensionNode,
+    InputValueDefinitionNode,
+    InterfaceTypeDefinitionNode,
+    InterfaceTypeExtensionNode,
+    ObjectTypeDefinitionNode,
+    ObjectTypeExtensionNode,
+    ScalarTypeDefinitionNode,
+    ScalarTypeExtensionNode,
+    StringValueNode,
+    TypeDefinitionNode,
+    TypeExtensionNode,
+    ValueNode,
+    UnionTypeDefinitionNode,
+    UnionTypeExtensionNode,
+)
+from graphql.pyutils import FrozenList, Undefined
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLEnumValue,
+    GraphQLEnumType,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLScalarType,
+    GraphQLString,
+    GraphQLUnionType,
+)
+
+ScalarType = GraphQLScalarType("Scalar")
+ObjectType = GraphQLObjectType("Object", {})
+InterfaceType = GraphQLInterfaceType("Interface")
+UnionType = GraphQLUnionType(
+    "Union",
+    [ObjectType],
+    resolve_type=lambda _obj, _info, _type: None,  # pragma: no cover
+)
+EnumType = GraphQLEnumType("Enum", {"foo": GraphQLEnumValue()})
+InputObjectType = GraphQLInputObjectType("InputObject", {})
+
+ListOfScalarsType = GraphQLList(ScalarType)
+NonNullScalarType = GraphQLNonNull(ScalarType)
+ListOfNonNullScalarsType = GraphQLList(NonNullScalarType)
+NonNullListOfScalars = GraphQLNonNull(ListOfScalarsType)
+
+
+def describe_type_system_scalars():
+    def defines_a_scalar_type():
+        scalar = GraphQLScalarType("SomeScalar")
+        assert scalar.name == "SomeScalar"
+        kwargs = scalar.to_kwargs()
+        assert kwargs == {
+            "name": "SomeScalar",
+            "description": None,
+            "specified_by_url": None,
+            "serialize": None,
+            "parse_value": None,
+            "parse_literal": None,
+            "extensions": None,
+            "ast_node": None,
+            "extension_ast_nodes": [],
+        }
+
+    def accepts_a_scalar_type_defining_serialize():
+        def serialize(value):
+            pass
+
+        scalar = GraphQLScalarType("SomeScalar", serialize)
+        assert scalar.serialize is serialize
+        assert scalar.to_kwargs()["serialize"] is serialize
+
+    def defines_a_scalar_type_with_a_description():
+        description = "nice scalar"
+        scalar = GraphQLScalarType("SomeScalar", description=description)
+        assert scalar.description is description
+        assert scalar.to_kwargs()["description"] is description
+
+    def accepts_a_scalar_type_defining_specified_by_url():
+        url = "https://example.com/foo_spec"
+        scalar = GraphQLScalarType("SomeScalar", specified_by_url=url)
+        assert scalar.specified_by_url == url
+        assert scalar.to_kwargs()["specified_by_url"] == url
+
+    def accepts_a_scalar_type_defining_parse_value_and_parse_literal():
+        def parse_value(_value):
+            pass
+
+        def parse_literal(_value_node, _variables):
+            pass
+
+        scalar = GraphQLScalarType(
+            "SomeScalar", parse_value=parse_value, parse_literal=parse_literal
+        )
+        assert scalar.parse_value is parse_value
+        assert scalar.parse_literal is parse_literal
+
+        kwargs = scalar.to_kwargs()
+        assert kwargs["parse_value"] is parse_value
+        assert kwargs["parse_literal"] is parse_literal
+
+    def provides_default_methods_if_omitted():
+        scalar = GraphQLScalarType("Foo")
+
+        assert scalar.serialize is GraphQLScalarType.serialize
+        assert scalar.parse_value is GraphQLScalarType.parse_value
+        assert (
+            scalar.parse_literal.__func__  # type: ignore
+            is GraphQLScalarType.parse_literal
+        )
+
+        kwargs = scalar.to_kwargs()
+        assert kwargs["serialize"] is None
+        assert kwargs["parse_value"] is None
+        assert kwargs["parse_literal"] is None
+
+    def use_parse_value_for_parsing_literals_if_parse_literal_omitted():
+        scalar = GraphQLScalarType(
+            "Foo", parse_value=lambda value: f"parse_value: {value!r}"
+        )
+
+        assert scalar.parse_literal(parse_value("null")) == "parse_value: None"
+        assert (
+            scalar.parse_literal(parse_value('{foo: "bar"}'))
+            == "parse_value: {'foo': 'bar'}"
+        )
+
+    def accepts_a_scalar_type_with_ast_node_and_extension_ast_nodes():
+        ast_node = ScalarTypeDefinitionNode()
+        extension_ast_nodes = [ScalarTypeExtensionNode()]
+        scalar = GraphQLScalarType(
+            "SomeScalar", ast_node=ast_node, extension_ast_nodes=extension_ast_nodes
+        )
+        assert scalar.ast_node is ast_node
+        assert isinstance(scalar.extension_ast_nodes, FrozenList)
+        assert scalar.extension_ast_nodes == extension_ast_nodes
+        extension_ast_nodes = scalar.extension_ast_nodes
+        scalar = GraphQLScalarType(
+            "SomeScalar", ast_node=None, extension_ast_nodes=extension_ast_nodes
+        )
+        assert scalar.ast_node is None
+        assert scalar.extension_ast_nodes is extension_ast_nodes
+
+    def rejects_a_scalar_type_without_a_name():
+        with raises(TypeError, match="missing .* required .* 'name'"):
+            # noinspection PyArgumentList
+            GraphQLScalarType()  # type: ignore
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType(None)  # type: ignore
+        assert str(exc_info.value) == "Must provide name."
+        with raises(TypeError) as exc_info:
+            GraphQLScalarType("")
+        assert str(exc_info.value) == "Must provide name."
+
+    def rejects_a_scalar_type_with_incorrectly_typed_name():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType(name=42)  # type: ignore
+        assert str(exc_info.value) == "The name must be a string."
+
+    def rejects_a_scalar_type_with_incorrectly_typed_description():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType("SomeScalar", description=[])  # type: ignore
+        assert str(exc_info.value) == "The description must be a string."
+
+    def rejects_a_scalar_type_defining_specified_by_url_with_an_incorrect_type():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType("SomeScalar", specified_by_url={})  # type: ignore
+        assert (
+            str(exc_info.value)
+            == "SomeScalar must provide 'specified_by_url' as a string, but got: {}."
+        )
+
+    def rejects_a_scalar_type_defining_serialize_with_incorrect_type():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType("SomeScalar", {})  # type: ignore
+        assert str(exc_info.value) == (
+            "SomeScalar must provide 'serialize' as a function."
+            " If this custom Scalar is also used as an input type,"
+            " ensure 'parse_value' and 'parse_literal' functions"
+            " are also provided."
+        )
+
+    def rejects_a_scalar_type_defining_parse_literal_but_not_parse_value():
+        def parse_literal(_node: ValueNode, _vars=None):
+            return Undefined  # pragma: no cover
+
+        with raises(TypeError) as exc_info:
+            GraphQLScalarType("SomeScalar", parse_literal=parse_literal)
+        assert str(exc_info.value) == (
+            "SomeScalar must provide both"
+            " 'parse_value' and 'parse_literal' as functions."
+        )
+
+    def rejects_a_scalar_type_incorrectly_defining_parse_literal_and_value():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType(
+                "SomeScalar", parse_value={}, parse_literal={}  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeScalar must provide both"
+            " 'parse_value' and 'parse_literal' as functions."
+        )
+
+    def rejects_a_scalar_type_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType("SomeScalar", ast_node=Node())  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "SomeScalar AST node must be a TypeDefinitionNode."
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType(
+                "SomeScalar", ast_node=TypeDefinitionNode()  # type: ignore
+            )
+        msg = str(exc_info.value)
+        assert msg == "SomeScalar AST node must be a ScalarTypeDefinitionNode."
+
+    def rejects_a_scalar_type_with_incorrect_extension_ast_nodes():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType(
+                "SomeScalar", extension_ast_nodes=[Node()]  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeScalar extension AST nodes must be specified"
+            " as a collection of TypeExtensionNode instances."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLScalarType(
+                "SomeScalar", extension_ast_nodes=[TypeExtensionNode()]  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeScalar extension AST nodes must be specified"
+            " as a collection of ScalarTypeExtensionNode instances."
+        )
+
+
+def describe_type_system_fields():
+    def defines_a_field():
+        field = GraphQLField(GraphQLString)
+        assert field.type is GraphQLString
+        kwargs = field.to_kwargs()
+        assert kwargs == {
+            "type_": GraphQLString,
+            "args": None,
+            "resolve": None,
+            "subscribe": None,
+            "description": None,
+            "deprecation_reason": None,
+            "extensions": None,
+            "ast_node": None,
+        }
+
+    def defines_a_field_with_args():
+        arg = GraphQLArgument(GraphQLInt)
+        field = GraphQLField(GraphQLString, {"arg": arg})
+        assert isinstance(field.args, dict)
+        assert list(field.args) == ["arg"]
+        assert field.args["arg"] is arg
+        assert field.to_kwargs()["args"] == {"arg": arg}
+
+    def defines_a_field_with_input_types_as_args():
+        field = GraphQLField(GraphQLString, {"arg": GraphQLString})  # type: ignore
+        assert isinstance(field.args, dict)
+        assert list(field.args) == ["arg"]
+        arg = field.args["arg"]
+        assert isinstance(arg, GraphQLArgument)
+        assert arg.type is GraphQLString
+
+    def defines_a_scalar_type_with_a_description():
+        description = "nice field"
+        field = GraphQLField(GraphQLString, description=description)
+        assert field.description is description
+        assert field.to_kwargs()["description"] is description
+
+    def defines_a_scalar_type_with_a_deprecation_reason():
+        deprecation_reason = "field is redundant"
+        field = GraphQLField(GraphQLString, deprecation_reason=deprecation_reason)
+        assert field.deprecation_reason is deprecation_reason
+        assert field.to_kwargs()["deprecation_reason"] is deprecation_reason
+
+    def rejects_a_field_with_incorrect_type():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLField(InputObjectType)  # type: ignore
+        assert str(exc_info.value) == "Field type must be an output type."
+
+    def rejects_a_field_with_incorrect_args():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLField(GraphQLString, args=[])  # type: ignore
+        assert str(exc_info.value) == (
+            "Field args must be a dict with argument names as keys."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLField(GraphQLString, args={"arg": GraphQLObjectType})  # type: ignore
+        assert str(exc_info.value) == (
+            "Field args must be GraphQLArguments or input type objects."
+        )
+
+    def rejects_a_field_with_an_incorrectly_typed_description():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLField(GraphQLString, description=[])  # type: ignore
+        assert str(exc_info.value) == "The description must be a string."
+
+    def rejects_a_field_with_an_incorrectly_typed_deprecation_reason():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLField(GraphQLString, deprecation_reason=[])  # type: ignore
+        assert str(exc_info.value) == "The deprecation reason must be a string."
+
+    def rejects_a_field_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLField(GraphQLString, ast_node=Node())  # type: ignore
+        assert str(exc_info.value) == "Field AST node must be a FieldDefinitionNode."
+
+
+def describe_type_system_objects():
+    def does_not_mutate_passed_field_definitions():
+        output_fields = {
+            "field1": GraphQLField(ScalarType),
+            "field2": GraphQLField(
+                ScalarType, args={"id": GraphQLArgument(ScalarType)}
+            ),
+        }
+
+        test_object_1 = GraphQLObjectType("Test1", output_fields)
+        test_object_2 = GraphQLObjectType("Test2", output_fields)
+
+        assert test_object_1.fields == test_object_2.fields
+        assert output_fields == {
+            "field1": GraphQLField(ScalarType),
+            "field2": GraphQLField(
+                ScalarType, args={"id": GraphQLArgument(ScalarType)}
+            ),
+        }
+
+        input_fields = {
+            "field1": GraphQLInputField(ScalarType),
+            "field2": GraphQLInputField(ScalarType),
+        }
+
+        test_input_object_1 = GraphQLInputObjectType("Test1", input_fields)
+        test_input_object_2 = GraphQLInputObjectType("Test2", input_fields)
+
+        assert test_input_object_1.fields == test_input_object_2.fields
+        assert input_fields == {
+            "field1": GraphQLInputField(ScalarType),
+            "field2": GraphQLInputField(ScalarType),
+        }
+
+    def defines_an_object_type_with_deprecated_field():
+        TypeWithDeprecatedField = GraphQLObjectType(
+            "foo",
+            {
+                "bar": GraphQLField(ScalarType, deprecation_reason="A terrible reason"),
+                "baz": GraphQLField(ScalarType, deprecation_reason=""),
+            },
+        )
+
+        deprecated_field = TypeWithDeprecatedField.fields["bar"]
+        assert deprecated_field == GraphQLField(
+            ScalarType, deprecation_reason="A terrible reason"
+        )
+        assert deprecated_field.is_deprecated is True
+        assert deprecated_field.deprecation_reason == "A terrible reason"
+
+        deprecated_field = TypeWithDeprecatedField.fields["baz"]
+        assert deprecated_field == GraphQLField(ScalarType, deprecation_reason="")
+        assert deprecated_field.is_deprecated is True
+        assert deprecated_field.deprecation_reason == ""
+
+    def accepts_an_object_type_with_output_type_as_field():
+        # this is a shortcut syntax for simple fields
+        obj_type = GraphQLObjectType("SomeObject", {"f": ScalarType})  # type: ignore
+        assert list(obj_type.fields) == ["f"]
+        field = obj_type.fields["f"]
+        assert isinstance(field, GraphQLField)
+        assert field.type is ScalarType
+        assert field.args == {}
+        assert field.is_deprecated is False
+
+    def accepts_an_object_type_with_a_field_function():
+        obj_type = GraphQLObjectType(
+            "SomeObject", lambda: {"f": GraphQLField(ScalarType)}
+        )
+        assert list(obj_type.fields) == ["f"]
+        field = obj_type.fields["f"]
+        assert isinstance(field, GraphQLField)
+        assert field.description is None
+        assert field.type is ScalarType
+        assert field.args == {}
+        assert field.resolve is None
+        assert field.subscribe is None
+        assert field.is_deprecated is False
+        assert field.deprecation_reason is None
+        assert field.extensions is None
+        assert field.ast_node is None
+
+    def thunk_for_fields_of_object_type_is_resolved_only_once():
+        calls = 0
+
+        def fields():
+            nonlocal calls
+            calls += 1
+            return {"f": GraphQLField(ScalarType)}
+
+        obj_type = GraphQLObjectType("SomeObject", fields)
+        assert "f" in obj_type.fields
+        assert calls == 1
+        assert "f" in obj_type.fields
+        assert calls == 1
+
+    def accepts_an_object_type_with_field_args():
+        obj_type = GraphQLObjectType(
+            "SomeObject",
+            {"f": GraphQLField(ScalarType, args={"arg": GraphQLArgument(ScalarType)})},
+        )
+        field = obj_type.fields["f"]
+        assert isinstance(field, GraphQLField)
+        assert field.description is None
+        assert field.type is ScalarType
+        assert isinstance(field.args, dict)
+        assert list(field.args) == ["arg"]
+        arg = field.args["arg"]
+        assert isinstance(arg, GraphQLArgument)
+        assert arg.description is None
+        assert arg.type is ScalarType
+        assert arg.default_value is Undefined
+        assert arg.extensions is None
+        assert arg.ast_node is None
+        assert field.resolve is None
+        assert field.subscribe is None
+        assert field.is_deprecated is False
+        assert field.deprecation_reason is None
+        assert field.extensions is None
+        assert field.ast_node is None
+
+    def accepts_an_object_type_with_list_interfaces():
+        obj_type = GraphQLObjectType("SomeObject", {}, [InterfaceType])
+        assert obj_type.interfaces == [InterfaceType]
+
+    def accepts_object_type_with_interfaces_as_a_function_returning_a_list():
+        obj_type = GraphQLObjectType("SomeObject", {}, lambda: [InterfaceType])
+        assert obj_type.interfaces == [InterfaceType]
+
+    def thunk_for_interfaces_of_object_type_is_resolved_only_once():
+        calls = 0
+
+        def interfaces():
+            nonlocal calls
+            calls += 1
+            return [InterfaceType]
+
+        obj_type = GraphQLObjectType("SomeObject", {}, interfaces)
+        assert obj_type.interfaces == [InterfaceType]
+        assert calls == 1
+        assert obj_type.interfaces == [InterfaceType]
+        assert calls == 1
+
+    def accepts_a_lambda_as_an_object_field_resolver():
+        obj_type = GraphQLObjectType(
+            "SomeObject",
+            {
+                "f": GraphQLField(
+                    ScalarType, resolve=lambda _obj, _info: {}  # pragma: no cover
+                )
+            },
+        )
+        assert obj_type.fields
+
+    def accepts_an_object_type_with_ast_node_and_extension_ast_nodes():
+        ast_node = ObjectTypeDefinitionNode()
+        extension_ast_nodes = [ObjectTypeExtensionNode()]
+        object_type = GraphQLObjectType(
+            "SomeObject",
+            {"f": GraphQLField(ScalarType)},
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert object_type.ast_node is ast_node
+        assert isinstance(object_type.extension_ast_nodes, FrozenList)
+        assert object_type.extension_ast_nodes == extension_ast_nodes
+        extension_ast_nodes = object_type.extension_ast_nodes
+        object_type = GraphQLObjectType(
+            "SomeObject",
+            {"f": GraphQLField(ScalarType)},
+            ast_node=None,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert object_type.ast_node is None
+        assert object_type.extension_ast_nodes is extension_ast_nodes
+
+    def rejects_an_object_type_without_a_name():
+        with raises(TypeError, match="missing .* required .* 'name'"):
+            # noinspection PyArgumentList
+            GraphQLObjectType()  # type: ignore
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType(None, {})  # type: ignore
+        assert str(exc_info.value) == "Must provide name."
+        with raises(TypeError) as exc_info:
+            GraphQLObjectType("", {})
+        assert str(exc_info.value) == "Must provide name."
+
+    def rejects_an_object_type_field_with_undefined_config():
+        undefined_field = cast(GraphQLField, None)
+        obj_type = GraphQLObjectType("SomeObject", {"f": undefined_field})
+        with raises(TypeError) as exc_info:
+            if obj_type.fields:
+                pass
+        msg = str(exc_info.value)
+        assert msg == "SomeObject fields must be GraphQLField or output type objects."
+
+    def rejects_an_object_type_with_incorrectly_typed_fields():
+        invalid_field = cast(GraphQLField, [GraphQLField(ScalarType)])
+        obj_type = GraphQLObjectType("SomeObject", {"f": invalid_field})
+        with raises(TypeError) as exc_info:
+            if obj_type.fields:
+                pass
+        msg = str(exc_info.value)
+        assert msg == "SomeObject fields must be GraphQLField or output type objects."
+
+    def rejects_an_object_type_field_function_that_returns_incorrect_type():
+        obj_type = GraphQLObjectType(
+            "SomeObject", lambda: [GraphQLField(ScalarType)]  # type: ignore
+        )
+        with raises(TypeError) as exc_info:
+            if obj_type.fields:
+                pass
+        assert str(exc_info.value) == (
+            "SomeObject fields must be specified as a dict with field names as keys."
+        )
+
+    def rejects_an_object_type_field_function_that_raises_an_error():
+        def fields():
+            raise RuntimeError("Oops!")
+
+        obj_type = GraphQLObjectType("SomeObject", fields)
+        with raises(TypeError) as exc_info:
+            if obj_type.fields:
+                pass
+        assert str(exc_info.value) == "SomeObject fields cannot be resolved. Oops!"
+
+    def rejects_an_object_type_with_incorrectly_typed_field_args():
+        invalid_args = [{"bad_args": GraphQLArgument(ScalarType)}]
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType(
+                "SomeObject",
+                {
+                    "badField": GraphQLField(
+                        ScalarType, args=invalid_args  # type: ignore
+                    )
+                },
+            )
+        msg = str(exc_info.value)
+        assert msg == "Field args must be a dict with argument names as keys."
+
+    def rejects_an_object_with_is_deprecated_instead_of_deprecation_reason_on_field():
+        kwargs = dict(is_deprecated=True)
+        with raises(
+            TypeError, match="got an unexpected keyword argument 'is_deprecated'"
+        ):
+            GraphQLObjectType(
+                "OldObject",
+                {"field": GraphQLField(ScalarType, **kwargs)},  # type: ignore
+            )
+
+    def rejects_an_object_type_with_incorrectly_typed_interfaces():
+        obj_type = GraphQLObjectType("SomeObject", {}, interfaces={})
+        with raises(TypeError) as exc_info:
+            if obj_type.interfaces:
+                pass
+        assert str(exc_info.value) == (
+            "SomeObject interfaces must be specified"
+            " as a collection of GraphQLInterfaceType instances."
+        )
+
+    def rejects_object_type_with_incorrectly_typed_interfaces_as_a_function():
+        obj_type = GraphQLObjectType("SomeObject", {}, interfaces=lambda: {})
+        with raises(TypeError) as exc_info:
+            if obj_type.interfaces:
+                pass
+        assert str(exc_info.value) == (
+            "SomeObject interfaces must be specified"
+            " as a collection of GraphQLInterfaceType instances."
+        )
+
+    def rejects_object_type_with_interfaces_as_function_that_raises_an_error():
+        def interfaces():
+            raise RuntimeError("Oops!")
+
+        obj_type = GraphQLObjectType("SomeObject", {}, interfaces=interfaces)
+        with raises(TypeError) as exc_info:
+            if obj_type.interfaces:
+                pass
+        assert str(exc_info.value) == "SomeObject interfaces cannot be resolved. Oops!"
+
+    def rejects_an_empty_object_field_resolver():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType(
+                "SomeObject",
+                {"field": GraphQLField(ScalarType, resolve={})},  # type: ignore
+            )
+        msg = str(exc_info.value)
+        assert msg == "Field resolver must be a function if provided,  but got: {}."
+
+    def rejects_a_constant_scalar_value_resolver():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType(
+                "SomeObject",
+                {"field": GraphQLField(ScalarType, resolve=0)},  # type: ignore
+            )
+        msg = str(exc_info.value)
+        assert msg == "Field resolver must be a function if provided,  but got: 0."
+
+    def rejects_an_object_type_with_an_incorrect_type_for_is_type_of():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType("AnotherObject", {}, is_type_of={})  # type: ignore
+        assert str(exc_info.value) == (
+            "AnotherObject must provide 'is_type_of' as a function, but got: {}."
+        )
+
+    def rejects_an_object_type_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType("SomeObject", {}, ast_node=Node())  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "SomeObject AST node must be a TypeDefinitionNode."
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType(
+                "SomeObject", {}, ast_node=TypeDefinitionNode()  # type: ignore
+            )
+        msg = str(exc_info.value)
+        assert msg == "SomeObject AST node must be an ObjectTypeDefinitionNode."
+
+    def rejects_an_object_type_with_incorrect_extension_ast_nodes():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType(
+                "SomeObject", {}, extension_ast_nodes=[Node()]  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeObject extension AST nodes must be specified"
+            " as a collection of TypeExtensionNode instances."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLObjectType(
+                "SomeObject",
+                {},
+                extension_ast_nodes=[TypeExtensionNode()],  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeObject extension AST nodes must be specified"
+            " as a collection of ObjectTypeExtensionNode instances."
+        )
+
+
+def describe_type_system_interfaces():
+    def defines_an_interface_type():
+        fields = {"f": GraphQLField(ScalarType)}
+        interface = GraphQLInterfaceType("AnotherInterface", fields)
+        assert interface.name == "AnotherInterface"
+        assert interface.fields == fields
+        assert interface.fields is not fields
+        assert interface.resolve_type is None
+        assert interface.extensions is None
+        kwargs = interface.to_kwargs()
+        assert kwargs == {
+            "name": "AnotherInterface",
+            "description": None,
+            "fields": fields,
+            "interfaces": [],
+            "resolve_type": None,
+            "extensions": None,
+            "ast_node": None,
+            "extension_ast_nodes": [],
+        }
+
+    def accepts_an_interface_type_defining_resolve_type():
+        def resolve_type(_obj, _info, _type):
+            pass
+
+        interface = GraphQLInterfaceType(
+            "AnotherInterface", {}, resolve_type=resolve_type
+        )
+        assert interface.resolve_type is resolve_type
+
+    def accepts_an_interface_type_with_output_types_as_fields():
+        interface = GraphQLInterfaceType(
+            "AnotherInterface", {"someField": ScalarType}  # type: ignore
+        )
+        fields = interface.fields
+        assert isinstance(fields, dict)
+        assert list(fields) == ["someField"]
+        field = fields["someField"]
+        assert isinstance(field, GraphQLField)
+        assert field.type is ScalarType
+
+    def accepts_an_interface_type_with_a_field_function():
+        fields = {"f": GraphQLField(ScalarType)}
+        interface = GraphQLInterfaceType("AnotherInterface", lambda: fields)
+        assert interface.fields == fields
+
+    def thunk_for_fields_of_interface_type_is_resolved_only_once():
+        calls = 0
+
+        def fields():
+            nonlocal calls
+            calls += 1
+            return {"f": GraphQLField(ScalarType)}
+
+        interface = GraphQLInterfaceType("AnotherInterface", fields)
+        assert "f" in interface.fields
+        assert calls == 1
+        assert "f" in interface.fields
+        assert calls == 1
+
+    def accepts_an_interface_type_with_a_list_of_interfaces():
+        implementing = GraphQLInterfaceType(
+            "AnotherInterface", {}, interfaces=[InterfaceType]
+        )
+        assert implementing.interfaces == [InterfaceType]
+
+    def accepts_an_interface_type_with_an_interfaces_function():
+        implementing = GraphQLInterfaceType(
+            "AnotherInterface", {}, interfaces=lambda: [InterfaceType]
+        )
+        assert implementing.interfaces == [InterfaceType]
+
+    def thunk_for_interfaces_of_interface_type_is_resolved_only_once():
+        calls = 0
+
+        def interfaces():
+            nonlocal calls
+            calls += 1
+            return [InterfaceType]
+
+        implementing = GraphQLInterfaceType(
+            "AnotherInterface", {}, interfaces=interfaces
+        )
+        assert implementing.interfaces == [InterfaceType]
+        assert calls == 1
+        assert implementing.interfaces == [InterfaceType]
+        assert calls == 1
+
+    def accepts_an_interface_type_with_ast_node_and_extension_ast_nodes():
+        ast_node = InterfaceTypeDefinitionNode()
+        extension_ast_nodes = [InterfaceTypeExtensionNode()]
+        interface_type = GraphQLInterfaceType(
+            "SomeInterface",
+            {"f": GraphQLField(ScalarType)},
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert interface_type.ast_node is ast_node
+        assert isinstance(interface_type.extension_ast_nodes, FrozenList)
+        assert interface_type.extension_ast_nodes == extension_ast_nodes
+        extension_ast_nodes = interface_type.extension_ast_nodes
+        interface_type = GraphQLInterfaceType(
+            "SomeInterface",
+            {"f": GraphQLField(ScalarType)},
+            ast_node=None,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert interface_type.ast_node is None
+        assert interface_type.extension_ast_nodes is extension_ast_nodes
+
+    def rejects_an_interface_type_with_incorrectly_typed_fields():
+        interface = GraphQLInterfaceType("SomeInterface", [])  # type: ignore
+        with raises(TypeError) as exc_info:
+            if interface.fields:
+                pass
+        assert str(exc_info.value) == (
+            "SomeInterface fields must be specified as a dict with field names as keys."
+        )
+        interface = GraphQLInterfaceType(
+            "SomeInterface", {"f": InputObjectType}  # type: ignore
+        )
+        with raises(TypeError) as exc_info:
+            if interface.fields:
+                pass
+        assert str(exc_info.value) == (
+            "SomeInterface fields must be GraphQLField or output type objects."
+        )
+
+    def rejects_an_interface_type_with_unresolvable_fields():
+        def fields():
+            raise RuntimeError("Oops!")
+
+        interface = GraphQLInterfaceType("SomeInterface", fields)
+        with raises(TypeError) as exc_info:
+            if interface.fields:
+                pass
+        assert str(exc_info.value) == "SomeInterface fields cannot be resolved. Oops!"
+
+    def rejects_an_interface_type_without_a_name():
+        with raises(TypeError, match="missing .* required .* 'name'"):
+            # noinspection PyArgumentList
+            GraphQLInterfaceType()  # type: ignore
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInterfaceType(None)  # type: ignore
+        assert str(exc_info.value) == "Must provide name."
+        with raises(TypeError) as exc_info:
+            GraphQLInterfaceType("")
+        assert str(exc_info.value) == "Must provide name."
+
+    def rejects_an_interface_type_with_incorrectly_typed_interfaces():
+        interface = GraphQLInterfaceType("AnotherInterface", {}, lambda: {})
+        with raises(TypeError) as exc_info:
+            if interface.interfaces:
+                pass
+        assert str(exc_info.value) == (
+            "AnotherInterface interfaces must be specified"
+            " as a collection of GraphQLInterfaceType instances."
+        )
+
+    def rejects_an_interface_type_with_unresolvable_interfaces():
+        def interfaces():
+            raise RuntimeError("Oops!")
+
+        interface = GraphQLInterfaceType("AnotherInterface", {}, interfaces)
+        with raises(TypeError) as exc_info:
+            if interface.interfaces:
+                pass
+        assert (
+            str(exc_info.value)
+            == "AnotherInterface interfaces cannot be resolved. Oops!"
+        )
+
+    def rejects_an_interface_type_with_an_incorrect_type_for_resolve_type():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInterfaceType(
+                "AnotherInterface", {}, resolve_type={}  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "AnotherInterface must provide 'resolve_type' as a function,"
+            " but got: {}."
+        )
+
+    def rejects_an_interface_type_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInterfaceType("SomeInterface", ast_node=Node())  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "SomeInterface AST node must be a TypeDefinitionNode."
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInterfaceType(
+                "SomeInterface", ast_node=TypeDefinitionNode()  # type: ignore
+            )
+        msg = str(exc_info.value)
+        assert msg == "SomeInterface AST node must be an InterfaceTypeDefinitionNode."
+
+    def rejects_an_interface_type_with_incorrect_extension_ast_nodes():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInterfaceType(
+                "SomeInterface", extension_ast_nodes=[Node()]  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeInterface extension AST nodes must be specified"
+            " as a collection of TypeExtensionNode instances."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInterfaceType(
+                "SomeInterface",
+                extension_ast_nodes=[TypeExtensionNode()],  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeInterface extension AST nodes must be specified"
+            " as a collection of InterfaceTypeExtensionNode instances."
+        )
+
+
+def describe_type_system_unions():
+    def accepts_a_union_type_defining_resolve_type():
+        assert GraphQLUnionType("SomeUnion", [ObjectType])
+
+    def accepts_a_union_type_with_list_types():
+        union_type = GraphQLUnionType("SomeUnion", [ObjectType])
+        assert union_type.types == [ObjectType]
+
+    def accepts_a_union_type_with_function_returning_a_list_of_types():
+        union_type = GraphQLUnionType("SomeUnion", lambda: [ObjectType])
+        assert union_type.types == [ObjectType]
+
+    def accepts_a_union_type_without_types():
+        with raises(TypeError, match="missing 1 required positional argument: 'types'"):
+            # noinspection PyArgumentList
+            GraphQLUnionType("SomeUnion")  # type: ignore
+        union_type = GraphQLUnionType("SomeUnion", None)  # type: ignore
+        assert union_type.types == []
+        union_type = GraphQLUnionType("SomeUnion", [])
+        assert union_type.types == []
+
+    def accepts_a_union_type_with_ast_node_and_extension_ast_nodes():
+        ast_node = UnionTypeDefinitionNode()
+        extension_ast_nodes = [UnionTypeExtensionNode()]
+        union_type = GraphQLUnionType(
+            "SomeUnion",
+            [ObjectType],
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert union_type.ast_node is ast_node
+        assert isinstance(union_type.extension_ast_nodes, FrozenList)
+        assert union_type.extension_ast_nodes == extension_ast_nodes
+        extension_ast_nodes = union_type.extension_ast_nodes
+        union_type = GraphQLUnionType(
+            "SomeUnion",
+            [ObjectType],
+            ast_node=None,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert union_type.ast_node is None
+        assert union_type.extension_ast_nodes is extension_ast_nodes
+
+    def rejects_a_union_type_without_a_name():
+        with raises(TypeError, match="missing .* required .* 'name'"):
+            # noinspection PyArgumentList
+            GraphQLUnionType()  # type: ignore
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLUnionType(None, [])  # type: ignore
+        assert str(exc_info.value) == "Must provide name."
+        with raises(TypeError) as exc_info:
+            GraphQLUnionType("", [])
+        assert str(exc_info.value) == "Must provide name."
+
+    def rejects_a_union_type_with_an_incorrect_type_for_resolve_type():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLUnionType("SomeUnion", [], resolve_type={})  # type: ignore
+        assert str(exc_info.value) == (
+            "SomeUnion must provide 'resolve_type' as a function, but got: {}."
+        )
+
+    def rejects_a_union_type_with_incorrectly_typed_types():
+        union_type = GraphQLUnionType("SomeUnion", {"type": ObjectType})  # type: ignore
+        with raises(TypeError) as exc_info:
+            if union_type.types:
+                pass
+        assert str(exc_info.value) == (
+            "SomeUnion types must be specified"
+            " as a collection of GraphQLObjectType instances."
+        )
+
+    def rejects_a_union_type_with_unresolvable_types():
+        def types():
+            raise RuntimeError("Oops!")
+
+        union_type = GraphQLUnionType("SomeUnion", types)
+        with raises(TypeError) as exc_info:
+            if union_type.types:
+                pass
+        assert str(exc_info.value) == "SomeUnion types cannot be resolved. Oops!"
+
+    def rejects_a_union_type_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLUnionType("SomeUnion", [], ast_node=Node())  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "SomeUnion AST node must be a TypeDefinitionNode."
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLUnionType(
+                "SomeUnion", [], ast_node=TypeDefinitionNode()  # type: ignore
+            )
+        msg = str(exc_info.value)
+        assert msg == "SomeUnion AST node must be a UnionTypeDefinitionNode."
+
+    def rejects_a_union_type_with_incorrect_extension_ast_nodes():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLUnionType(
+                "SomeUnion", [], extension_ast_nodes=[Node()]  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeUnion extension AST nodes must be specified"
+            " as a collection of TypeExtensionNode instances."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLUnionType(
+                "SomeUnion",
+                [],
+                extension_ast_nodes=[TypeExtensionNode()],  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeUnion extension AST nodes must be specified"
+            " as a collection of UnionTypeExtensionNode instances."
+        )
+
+
+def describe_type_system_enums():
+    def defines_an_enum_type_with_a_description():
+        description = "nice enum"
+        enum_type = GraphQLEnumType(
+            "SomeEnum", {}, description=description  # type: ignore
+        )
+        assert enum_type.description is description
+        assert enum_type.to_kwargs()["description"] is description
+
+    def defines_an_enum_type_with_deprecated_value():
+        EnumTypeWithDeprecatedValue = GraphQLEnumType(
+            name="EnumWithDeprecatedValue",
+            values={"foo": GraphQLEnumValue(deprecation_reason="Just because")},
+        )
+
+        deprecated_value = EnumTypeWithDeprecatedValue.values["foo"]
+        assert deprecated_value == GraphQLEnumValue(deprecation_reason="Just because")
+        assert deprecated_value.is_deprecated is True
+        assert deprecated_value.deprecation_reason == "Just because"
+        assert deprecated_value.value is None
+        assert deprecated_value.extensions is None
+        assert deprecated_value.ast_node is None
+
+    def defines_an_enum_type_with_a_value_of_none_and_invalid():
+        EnumTypeWithNullishValue = GraphQLEnumType(
+            name="EnumWithNullishValue",
+            values={"NULL": None, "NAN": nan, "NO_CUSTOM_VALUE": Undefined},
+        )
+
+        assert list(EnumTypeWithNullishValue.values) == [
+            "NULL",
+            "NAN",
+            "NO_CUSTOM_VALUE",
+        ]
+        null_value = EnumTypeWithNullishValue.values["NULL"]
+        assert null_value.description is None
+        assert null_value.value is None
+        assert null_value.is_deprecated is False
+        assert null_value.deprecation_reason is None
+        assert null_value.extensions is None
+        assert null_value.ast_node is None
+        null_value = EnumTypeWithNullishValue.values["NAN"]
+        assert null_value.description is None
+        assert isnan(null_value.value)
+        assert null_value.is_deprecated is False
+        assert null_value.deprecation_reason is None
+        assert null_value.extensions is None
+        assert null_value.ast_node is None
+        no_custom_value = EnumTypeWithNullishValue.values["NO_CUSTOM_VALUE"]
+        assert no_custom_value.description is None
+        assert no_custom_value.value is Undefined
+        assert no_custom_value.is_deprecated is False
+        assert no_custom_value.deprecation_reason is None
+        assert no_custom_value.extensions is None
+        assert no_custom_value.ast_node is None
+
+    def accepts_a_well_defined_enum_type_with_empty_value_definition():
+        enum_type = GraphQLEnumType("SomeEnum", {"FOO": None, "BAR": None})
+        assert enum_type.values["FOO"].value is None
+        assert enum_type.values["BAR"].value is None
+
+    def accepts_a_well_defined_enum_type_with_internal_value_definition():
+        enum_type = GraphQLEnumType("SomeEnum", {"FOO": 10, "BAR": 20})
+        assert enum_type.values["FOO"].value == 10
+        assert enum_type.values["BAR"].value == 20
+        enum_type = GraphQLEnumType(
+            "SomeEnum", {"FOO": GraphQLEnumValue(10), "BAR": GraphQLEnumValue(20)}
+        )
+        assert enum_type.values["FOO"].value == 10
+        assert enum_type.values["BAR"].value == 20
+
+    def serializes_an_enum():
+        enum_type = GraphQLEnumType(
+            "SomeEnum", {"FOO": "fooValue", "BAR": ["barValue"], "BAZ": None}
+        )
+        assert enum_type.values["FOO"].value == "fooValue"
+        assert enum_type.values["BAR"].value == ["barValue"]
+        assert enum_type.values["BAZ"].value is None
+        with raises(GraphQLError) as exc_info:
+            enum_type.serialize(None)
+        msg = exc_info.value.message
+        assert msg == "Enum 'SomeEnum' cannot represent value: None"
+        with raises(GraphQLError) as exc_info:
+            enum_type.serialize(Undefined)
+        msg = exc_info.value.message
+        assert msg == "Enum 'SomeEnum' cannot represent value: Undefined"
+        assert enum_type.serialize("fooValue") == "FOO"
+        with raises(GraphQLError) as exc_info:
+            enum_type.serialize("FOO")
+        msg = exc_info.value.message
+        assert msg == "Enum 'SomeEnum' cannot represent value: 'FOO'"
+        assert enum_type.serialize(["barValue"]) == "BAR"
+        with raises(GraphQLError) as exc_info:
+            enum_type.serialize("BAR")
+        msg = exc_info.value.message
+        assert msg == "Enum 'SomeEnum' cannot represent value: 'BAR'"
+        assert enum_type.serialize("BAZ") == "BAZ"
+        with raises(GraphQLError) as exc_info:
+            enum_type.serialize("bazValue")
+        msg = exc_info.value.message
+        assert msg == "Enum 'SomeEnum' cannot represent value: 'bazValue'"
+        with raises(GraphQLError) as exc_info:
+            enum_type.serialize(["bazValue"])
+        msg = exc_info.value.message
+        assert msg == "Enum 'SomeEnum' cannot represent value: ['bazValue']"
+
+    def use_first_name_for_duplicate_values():
+        enum_type = GraphQLEnumType("SomeEnum", {"FOO": "fooValue", "BAR": "fooValue"})
+        assert enum_type.values["FOO"].value == "fooValue"
+        assert enum_type.values["BAR"].value == "fooValue"
+        assert enum_type.serialize("fooValue") == "FOO"
+
+    def parses_an_enum():
+        enum_type = GraphQLEnumType(
+            "SomeEnum", {"FOO": "fooValue", "BAR": ["barValue"], "BAZ": None}
+        )
+        assert enum_type.parse_value("FOO") == "fooValue"
+        with raises(GraphQLError) as exc_info:
+            enum_type.parse_value("fooValue")
+        msg = exc_info.value.message
+        assert msg == "Value 'fooValue' does not exist in 'SomeEnum' enum."
+        assert enum_type.parse_value("BAR") == ["barValue"]
+        with raises(GraphQLError) as exc_info:
+            # noinspection PyTypeChecker
+            enum_type.parse_value(["barValue"])  # type: ignore
+        msg = exc_info.value.message
+        assert msg == "Enum 'SomeEnum' cannot represent non-string value: ['barValue']."
+        assert enum_type.parse_value("BAZ") is None
+        assert enum_type.parse_literal(EnumValueNode(value="FOO")) == "fooValue"
+        with raises(GraphQLError) as exc_info:
+            enum_type.parse_literal(StringValueNode(value="FOO"))
+        assert exc_info.value.message == (
+            "Enum 'SomeEnum' cannot represent non-enum value: \"FOO\"."
+            " Did you mean the enum value 'FOO'?"
+        )
+        with raises(GraphQLError) as exc_info:
+            enum_type.parse_literal(EnumValueNode(value="fooValue"))
+        msg = exc_info.value.message
+        assert msg == "Value 'fooValue' does not exist in 'SomeEnum' enum."
+        assert enum_type.parse_literal(EnumValueNode(value="BAR")) == ["barValue"]
+        with raises(GraphQLError) as exc_info:
+            enum_type.parse_literal(StringValueNode(value="BAR"))
+        assert exc_info.value.message == (
+            "Enum 'SomeEnum' cannot represent non-enum value: \"BAR\"."
+            " Did you mean the enum value 'BAR' or 'BAZ'?"
+        )
+        assert enum_type.parse_literal(EnumValueNode(value="BAZ")) is None
+        with raises(GraphQLError) as exc_info:
+            enum_type.parse_literal(StringValueNode(value="BAZ"))
+        assert exc_info.value.message == (
+            "Enum 'SomeEnum' cannot represent non-enum value: \"BAZ\"."
+            " Did you mean the enum value 'BAZ' or 'BAR'?"
+        )
+
+    def accepts_an_enum_type_with_ast_node_and_extension_ast_nodes():
+        ast_node = EnumTypeDefinitionNode()
+        extension_ast_nodes = [EnumTypeExtensionNode()]
+        enum_type = GraphQLEnumType(
+            "SomeEnum",
+            {},  # type: ignore
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert enum_type.ast_node is ast_node
+        assert isinstance(enum_type.extension_ast_nodes, FrozenList)
+        assert enum_type.extension_ast_nodes == extension_ast_nodes
+        extension_ast_nodes = enum_type.extension_ast_nodes
+        enum_type = GraphQLEnumType(
+            "SomeEnum",
+            {},  # type: ignore
+            ast_node=None,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert enum_type.ast_node is None
+        assert enum_type.extension_ast_nodes is extension_ast_nodes
+
+    def rejects_an_enum_type_without_a_name():
+        with raises(TypeError, match="missing .* required .* 'name'"):
+            # noinspection PyArgumentList
+            GraphQLEnumType(values={})  # type: ignore
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType(None, values={})  # type: ignore
+        assert str(exc_info.value) == "Must provide name."
+        with raises(TypeError) as exc_info:
+            GraphQLEnumType("", values={})  # type: ignore
+        assert str(exc_info.value) == "Must provide name."
+
+    def rejects_an_enum_type_with_incorrectly_typed_name():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType(name=42, values={})  # type: ignore
+        assert str(exc_info.value) == "The name must be a string."
+
+    def rejects_an_enum_type_without_values():
+        with raises(TypeError, match="missing .* required .* 'values'"):
+            # noinspection PyArgumentList
+            GraphQLEnumType("SomeEnum")  # type: ignore
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType("SomeEnum", values=None)  # type: ignore
+        assert str(exc_info.value) == (
+            "SomeEnum values must be an Enum or a dict with value names as keys."
+        )
+
+    def rejects_an_enum_type_with_incorrectly_typed_values():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType("SomeEnum", [{"FOO": 10}])  # type: ignore
+        assert str(exc_info.value) == (
+            "SomeEnum values must be an Enum or a dict with value names as keys."
+        )
+
+    def does_not_allow_is_deprecated_instead_of_deprecation_reason_on_enum():
+        with raises(
+            TypeError, match="got an unexpected keyword argument 'is_deprecated'"
+        ):
+            # noinspection PyArgumentList
+            GraphQLEnumType(
+                "SomeEnum",
+                {"FOO": GraphQLEnumValue(is_deprecated=True)},  # type: ignore
+            )
+
+    def rejects_an_enum_type_with_an_incorrectly_typed_description():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType("SomeEnum", {"foo": None}, description=[])  # type: ignore
+        assert str(exc_info.value) == "The description must be a string."
+
+    def rejects_an_enum_type_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType("SomeEnum", {"foo": None}, ast_node=Node())  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "SomeEnum AST node must be a TypeDefinitionNode."
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType(
+                "SomeEnum", {"foo": None}, ast_node=TypeDefinitionNode()  # type: ignore
+            )
+        msg = str(exc_info.value)
+        assert msg == "SomeEnum AST node must be an EnumTypeDefinitionNode."
+
+    def rejects_an_enum_type_with_incorrect_extension_ast_nodes():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType(
+                "SomeEnum", {"foo": None}, extension_ast_nodes=[Node()]  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeEnum extension AST nodes must be specified"
+            " as a collection of TypeExtensionNode instances."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLEnumType(
+                "SomeEnum",
+                {"foo": None},
+                extension_ast_nodes=[TypeExtensionNode()],  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeEnum extension AST nodes must be specified"
+            " as a collection of EnumTypeExtensionNode instances."
+        )
+
+    def describe_enum_values():
+        def accepts_an_enum_value_without_value():
+            enum_value = GraphQLEnumValue()
+            assert enum_value.value is None
+            assert enum_value.to_kwargs()["value"] is None
+
+        def accepts_an_enum_value_with_a_value():
+            value = object()
+            enum_value = GraphQLEnumValue(value)
+            assert enum_value.value is value
+            assert enum_value.to_kwargs()["value"] is value
+
+        def accepts_an_enum_value_with_a_description():
+            description = "nice enum value"
+            enum_value = GraphQLEnumValue(description=description)
+            assert enum_value.description is description
+            assert enum_value.to_kwargs()["description"] is description
+
+        def accepts_an_enum_value_with_deprecation_reason():
+            deprecation_reason = "This has been overvalued"
+            enum_value = GraphQLEnumValue(deprecation_reason=deprecation_reason)
+            assert enum_value.deprecation_reason is deprecation_reason
+            assert enum_value.to_kwargs()["deprecation_reason"] is deprecation_reason
+
+        def can_compare_enum_values():
+            assert GraphQLEnumValue() == GraphQLEnumValue()
+            assert GraphQLEnumValue(
+                "value", description="description", deprecation_reason="reason"
+            ) == GraphQLEnumValue(
+                "value", description="description", deprecation_reason="reason"
+            )
+            assert GraphQLEnumValue("value 1") != GraphQLEnumValue("value 2")
+            assert GraphQLEnumValue(description="description 1") != GraphQLEnumValue(
+                description="description 2"
+            )
+            assert GraphQLEnumValue(deprecation_reason="reason 1") != GraphQLEnumValue(
+                deprecation_reason="reason 2"
+            )
+
+        def rejects_an_enum_value_with_an_incorrectly_typed_description():
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                GraphQLEnumValue(description=[])  # type: ignore
+            msg = str(exc_info.value)
+            assert msg == "The description of the enum value must be a string."
+
+        def rejects_an_enum_value_with_an_incorrectly_typed_deprecation_reason():
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                GraphQLEnumValue(deprecation_reason=[])  # type: ignore
+            msg = str(exc_info.value)
+            assert msg == "The deprecation reason for the enum value must be a string."
+
+        def rejects_an_enum_value_with_an_incorrect_ast_node():
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                GraphQLEnumValue(ast_node=TypeDefinitionNode())  # type: ignore
+            msg = str(exc_info.value)
+            assert msg == "AST node must be an EnumValueDefinitionNode."
+
+
+def describe_type_system_input_objects():
+    def accepts_an_input_object_type_with_a_description():
+        description = "nice input object"
+        input_obj_type = GraphQLInputObjectType(
+            "SomeInputObject", {}, description=description
+        )
+        assert input_obj_type.description is description
+        assert input_obj_type.to_kwargs()["description"] is description
+
+    def accepts_an_input_object_type_with_an_out_type_function():
+        # This is an extension of GraphQL.js.
+        input_obj_type = GraphQLInputObjectType("SomeInputObject", {}, out_type=dict)
+        assert input_obj_type.out_type is dict
+        assert input_obj_type.to_kwargs()["out_type"] is dict
+
+    def provides_default_out_type_if_omitted():
+        # This is an extension of GraphQL.js.
+        input_obj_type = GraphQLInputObjectType("SomeInputObject", {})
+        assert input_obj_type.out_type is GraphQLInputObjectType.out_type
+        assert input_obj_type.to_kwargs()["out_type"] is None
+
+    def accepts_an_input_object_type_with_ast_node_and_extension_ast_nodes():
+        ast_node = InputObjectTypeDefinitionNode()
+        extension_ast_nodes = [InputObjectTypeExtensionNode()]
+        input_obj_type = GraphQLInputObjectType(
+            "SomeInputObject",
+            {},
+            ast_node=ast_node,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert input_obj_type.ast_node is ast_node
+        assert isinstance(input_obj_type.extension_ast_nodes, FrozenList)
+        assert input_obj_type.extension_ast_nodes == extension_ast_nodes
+        extension_ast_nodes = input_obj_type.extension_ast_nodes
+        input_obj_type = GraphQLInputObjectType(
+            "SomeInputObject",
+            {},
+            ast_node=None,
+            extension_ast_nodes=extension_ast_nodes,
+        )
+        assert input_obj_type.ast_node is None
+        assert input_obj_type.extension_ast_nodes is extension_ast_nodes
+
+    def rejects_an_input_object_type_with_incorrect_out_type_function():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputObjectType("SomeInputObject", {}, out_type=[])  # type: ignore
+        assert str(exc_info.value) == (
+            "The out type for SomeInputObject must be a function or a class."
+        )
+
+    def rejects_an_input_object_type_with_incorrectly_typed_description():
+        # noinspection PyTypeChecker
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputObjectType(
+                "SomeInputObject", {}, description=[]  # type: ignore
+            )
+        assert str(exc_info.value) == "The description must be a string."
+
+    def rejects_an_input_object_type_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputObjectType(
+                "SomeInputObject", {}, ast_node=Node()  # type: ignore
+            )
+        msg = str(exc_info.value)
+        assert msg == "SomeInputObject AST node must be a TypeDefinitionNode."
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputObjectType(
+                "SomeInputObject", {}, ast_node=TypeDefinitionNode()  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeInputObject AST node must be an InputObjectTypeDefinitionNode."
+        )
+
+    def rejects_an_input_object_type_with_incorrect_extension_ast_nodes():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputObjectType(
+                "SomeInputObject", {}, extension_ast_nodes=[Node()]  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeInputObject extension AST nodes must be specified"
+            " as a collection of TypeExtensionNode instances."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputObjectType(
+                "SomeInputObject",
+                {},
+                extension_ast_nodes=[TypeExtensionNode()],  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "SomeInputObject extension AST nodes must be specified"
+            " as a collection of InputObjectTypeExtensionNode instances."
+        )
+
+    def describe_input_objects_must_have_fields():
+        def accepts_an_input_object_type_with_fields():
+            input_obj_type = GraphQLInputObjectType(
+                "SomeInputObject", {"f": GraphQLInputField(ScalarType)}
+            )
+            assert list(input_obj_type.fields) == ["f"]
+            input_field = input_obj_type.fields["f"]
+            assert isinstance(input_field, GraphQLInputField)
+            assert input_field.description is None
+            assert input_field.type is ScalarType
+            assert input_field.default_value is Undefined
+            assert input_field.extensions is None
+            assert input_field.ast_node is None
+            assert input_field.out_name is None
+
+        def accepts_an_input_object_type_with_input_type_as_field():
+            # this is a shortcut syntax for simple input fields
+            input_obj_type = GraphQLInputObjectType(
+                "SomeInputObject", {"f": ScalarType}  # type: ignore
+            )
+            field = input_obj_type.fields["f"]
+            assert isinstance(field, GraphQLInputField)
+            assert field.type is ScalarType
+
+        def accepts_an_input_object_type_with_a_field_function():
+            input_obj_type = GraphQLInputObjectType(
+                "SomeInputObject", lambda: {"f": GraphQLInputField(ScalarType)}
+            )
+            assert list(input_obj_type.fields) == ["f"]
+            input_field = input_obj_type.fields["f"]
+            assert isinstance(input_field, GraphQLInputField)
+            assert input_field.description is None
+            assert input_field.type is ScalarType
+            assert input_field.default_value is Undefined
+            assert input_field.extensions is None
+            assert input_field.ast_node is None
+            assert input_field.out_name is None
+
+        def rejects_an_input_object_type_without_a_name():
+            with raises(TypeError, match="missing .* required .* 'name'"):
+                # noinspection PyArgumentList
+                GraphQLInputObjectType()  # type: ignore
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                GraphQLInputObjectType(None, {})  # type: ignore
+            assert str(exc_info.value) == "Must provide name."
+            with raises(TypeError) as exc_info:
+                GraphQLInputObjectType("", {})
+            assert str(exc_info.value) == "Must provide name."
+
+        def rejects_an_input_object_type_with_incorrect_fields():
+            input_obj_type = GraphQLInputObjectType(
+                "SomeInputObject", []  # type: ignore
+            )
+            with raises(TypeError) as exc_info:
+                if input_obj_type.fields:
+                    pass
+            assert str(exc_info.value) == (
+                "SomeInputObject fields must be specified"
+                " as a dict with field names as keys."
+            )
+
+        def rejects_an_input_object_type_with_incorrect_fields_function():
+            input_obj_type = GraphQLInputObjectType(
+                "SomeInputObject", lambda: []  # type: ignore
+            )
+            with raises(TypeError) as exc_info:
+                if input_obj_type.fields:
+                    pass
+            assert str(exc_info.value) == (
+                "SomeInputObject fields must be specified"
+                " as a dict with field names as keys."
+            )
+
+        def rejects_an_input_object_type_with_unresolvable_fields():
+            def fields():
+                raise RuntimeError("Oops!")
+
+            input_obj_type = GraphQLInputObjectType("SomeInputObject", fields)
+            with raises(TypeError) as exc_info:
+                if input_obj_type.fields:
+                    pass
+            assert str(exc_info.value) == (
+                "SomeInputObject fields cannot be resolved. Oops!"
+            )
+
+    def describe_input_objects_fields_must_not_have_resolvers():
+        def rejects_an_input_object_type_with_resolvers():
+            def resolve():
+                pass
+
+            with raises(
+                TypeError, match="got an unexpected keyword argument 'resolve'"
+            ):
+                # noinspection PyArgumentList
+                GraphQLInputObjectType(
+                    "SomeInputObject",
+                    {
+                        "f": GraphQLInputField(  # type: ignore
+                            ScalarType, resolve=resolve,
+                        )
+                    },
+                )
+            input_obj_type = GraphQLInputObjectType(
+                "SomeInputObject",
+                {
+                    "f": GraphQLField(  # type: ignore
+                        ScalarType, resolve=resolve
+                    )
+                },
+            )
+            with raises(TypeError) as exc_info:
+                if input_obj_type.fields:
+                    pass
+            assert str(exc_info.value) == (
+                "SomeInputObject fields must be GraphQLInputField"
+                " or input type objects."
+            )
+
+        def rejects_an_input_object_type_with_resolver_constant():
+            with raises(
+                TypeError, match="got an unexpected keyword argument 'resolve'"
+            ):
+                # noinspection PyArgumentList
+                GraphQLInputObjectType(
+                    "SomeInputObject",
+                    {"f": GraphQLInputField(ScalarType, resolve={})},  # type: ignore
+                )
+
+
+def describe_type_system_arguments():
+    def accepts_an_argument_with_a_description():
+        description = "nice argument"
+        argument = GraphQLArgument(GraphQLString, description=description)
+        assert argument.description is description
+        assert argument.to_kwargs()["description"] is description
+
+    def accepts_an_argument_with_an_out_name():
+        # This is an extension of GraphQL.js.
+        out_name = "python_rocks"
+        argument = GraphQLArgument(GraphQLString, out_name=out_name)
+        assert argument.out_name is out_name
+        assert argument.to_kwargs()["out_name"] is out_name
+
+    def provides_no_out_name_if_omitted():
+        # This is an extension of GraphQL.js.
+        argument = GraphQLArgument(GraphQLString)
+        assert argument.out_name is None
+        assert argument.to_kwargs()["out_name"] is None
+
+    def accepts_an_argument_with_an_ast_node():
+        ast_node = InputValueDefinitionNode()
+        argument = GraphQLArgument(GraphQLString, ast_node=ast_node)
+        assert argument.ast_node is ast_node
+        assert argument.to_kwargs()["ast_node"] is ast_node
+
+    def rejects_an_argument_without_type():
+        with raises(TypeError, match="missing 1 required positional argument"):
+            # noinspection PyArgumentList
+            GraphQLArgument()  # type: ignore
+
+    def rejects_an_argument_with_an_incorrect_type():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLArgument(GraphQLObjectType)  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "Argument type must be a GraphQL input type."
+
+    def rejects_an_argument_with_an_incorrectly_typed_description():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLArgument(GraphQLString, description=[])  # type: ignore
+        assert str(exc_info.value) == "Argument description must be a string."
+
+    def rejects_an_argument_with_an_incorrect_out_name():
+        # This is an extension of GraphQL.js.
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLArgument(GraphQLString, out_name=[])  # type: ignore
+        assert str(exc_info.value) == "Argument out name must be a string."
+
+    def rejects_an_argument_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLArgument(GraphQLString, ast_node=Node())  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "Argument AST node must be an InputValueDefinitionNode."
+
+
+def describe_type_system_input_fields():
+    def accepts_an_input_field_with_a_description():
+        description = "good input"
+        input_field = GraphQLInputField(GraphQLString, description=description)
+        assert input_field.description is description
+        assert input_field.to_kwargs()["description"] is description
+
+    def accepts_an_input_field_with_an_out_name():
+        # This is an extension of GraphQL.js.
+        out_name = "python_rocks"
+        input_field = GraphQLInputField(GraphQLString, out_name=out_name)
+        assert input_field.out_name is out_name
+        assert input_field.to_kwargs()["out_name"] is out_name
+
+    def provides_no_out_name_if_omitted():
+        # This is an extension of GraphQL.js.
+        input_field = GraphQLInputField(GraphQLString)
+        assert input_field.out_name is None
+        assert input_field.to_kwargs()["out_name"] is None
+
+    def accepts_an_input_field_with_an_ast_node():
+        ast_node = InputValueDefinitionNode()
+        input_field = GraphQLArgument(GraphQLString, ast_node=ast_node)
+        assert input_field.ast_node is ast_node
+        assert input_field.to_kwargs()["ast_node"] is ast_node
+
+    def rejects_an_input_field_without_type():
+        with raises(TypeError, match="missing 1 required positional argument"):
+            # noinspection PyArgumentList
+            GraphQLInputField()  # type: ignore
+
+    def rejects_an_input_field_with_an_incorrect_type():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputField(GraphQLObjectType)  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "Input field type must be a GraphQL input type."
+
+    def rejects_an_input_field_with_an_incorrectly_typed_description():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputField(GraphQLString, description=[])  # type: ignore
+        assert str(exc_info.value) == "Input field description must be a string."
+
+    def rejects_an_input_field_with_an_incorrect_out_name():
+        # This is an extension of GraphQL.js.
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputField(GraphQLString, out_name=[])  # type: ignore
+        assert str(exc_info.value) == "Input field out name must be a string."
+
+    def rejects_an_input_field_with_an_incorrect_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLInputField(GraphQLString, ast_node=Node())  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "Input field AST node must be an InputValueDefinitionNode."
+
+
+def describe_type_system_list():
+    types = [
+        ScalarType,
+        ObjectType,
+        UnionType,
+        InterfaceType,
+        EnumType,
+        InputObjectType,
+        ListOfScalarsType,
+        NonNullScalarType,
+    ]
+
+    @mark.parametrize("type_", types, ids=lambda type_: type_.__class__.__name__)
+    def accepts_a_type_as_item_type_of_list(type_):
+        assert GraphQLList(type_)
+
+    not_types = [{}, dict, str, object, None]
+
+    @mark.parametrize("type_", not_types, ids=lambda type_: repr(type_))
+    def rejects_a_non_type_as_item_type_of_list(type_):
+        with raises(TypeError) as exc_info:
+            GraphQLList(type_)
+        assert str(exc_info.value) == (
+            f"Can only create a wrapper for a GraphQLType, but got: {type_}."
+        )
+
+
+def describe_type_system_non_null():
+    types = [
+        ScalarType,
+        ObjectType,
+        UnionType,
+        InterfaceType,
+        EnumType,
+        InputObjectType,
+        ListOfScalarsType,
+        ListOfNonNullScalarsType,
+    ]
+
+    @mark.parametrize("type_", types, ids=lambda type_: type_.__class__.__name__)
+    def accepts_a_type_as_nullable_type_of_non_null(type_):
+        assert GraphQLNonNull(type_)
+
+    not_types = [NonNullScalarType, {}, dict, str, object, None]
+
+    @mark.parametrize("type_", not_types, ids=lambda type_: repr(type_))
+    def rejects_a_non_type_as_nullable_type_of_non_null(type_):
+        with raises(TypeError) as exc_info:
+            GraphQLNonNull(type_)
+        assert (
+            str(exc_info.value)
+            == (
+                "Can only create NonNull of a Nullable GraphQLType"
+                f" but got: {type_}."
+            )
+            if isinstance(type_, GraphQLNonNull)
+            else f"Can only create a wrapper for a GraphQLType, but got: {type_}."
+        )
+
+
+def describe_type_system_test_utility_methods():
+    def stringifies_simple_types():
+        assert str(ScalarType) == "Scalar"
+        assert str(ObjectType) == "Object"
+        assert str(InterfaceType) == "Interface"
+        assert str(UnionType) == "Union"
+        assert str(EnumType) == "Enum"
+        assert str(InputObjectType) == "InputObject"
+
+        assert str(NonNullScalarType) == "Scalar!"
+        assert str(ListOfScalarsType) == "[Scalar]"
+        assert str(NonNullListOfScalars) == "[Scalar]!"
+        assert str(ListOfNonNullScalarsType) == "[Scalar!]"
+        assert str(GraphQLList(ListOfScalarsType)) == "[[Scalar]]"
+
+    def simple_types_have_repr():
+        assert repr(ScalarType) == "<GraphQLScalarType 'Scalar'>"
+        assert repr(ObjectType) == "<GraphQLObjectType 'Object'>"
+        assert repr(InterfaceType) == "<GraphQLInterfaceType 'Interface'>"
+        assert repr(UnionType) == "<GraphQLUnionType 'Union'>"
+        assert repr(EnumType) == "<GraphQLEnumType 'Enum'>"
+        assert repr(InputObjectType) == "<GraphQLInputObjectType 'InputObject'>"
+        assert (
+            repr(ListOfNonNullScalarsType)
+            == "<GraphQLList <GraphQLNonNull <GraphQLScalarType 'Scalar'>>>"
+        )
+        assert (
+            repr(GraphQLList(ListOfScalarsType))
+            == "<GraphQLList <GraphQLList <GraphQLScalarType 'Scalar'>>>"
+        )
+
+    def stringifies_fields():
+        assert str(GraphQLField(GraphQLNonNull(GraphQLString))) == "Field: String!"
+        assert str(GraphQLField(GraphQLList(GraphQLInt))) == "Field: [Int]"
+
+    def fields_have_repr():
+        assert (
+            repr(GraphQLField(GraphQLNonNull(GraphQLString)))
+            == "<GraphQLField <GraphQLNonNull <GraphQLScalarType 'String'>>>"
+        )
+        assert (
+            repr(GraphQLField(GraphQLList(GraphQLInt)))
+            == "<GraphQLField <GraphQLList <GraphQLScalarType 'Int'>>>"
+        )
diff --git a/tests/type/test_directives.py b/tests/type/test_directives.py
new file mode 100644
index 0000000..83147cd
--- /dev/null
+++ b/tests/type/test_directives.py
@@ -0,0 +1,196 @@
+from pytest import raises  # type: ignore
+
+from graphql.language import DirectiveLocation, DirectiveDefinitionNode, Node
+from graphql.type import GraphQLArgument, GraphQLDirective, GraphQLInt, GraphQLString
+
+
+def describe_type_system_directive():
+    def can_create_instance():
+        arg = GraphQLArgument(GraphQLString, description="arg description")
+        node = DirectiveDefinitionNode()
+        locations = [DirectiveLocation.SCHEMA, DirectiveLocation.OBJECT]
+        directive = GraphQLDirective(
+            name="test",
+            locations=[DirectiveLocation.SCHEMA, DirectiveLocation.OBJECT],
+            args={"arg": arg},
+            description="test description",
+            is_repeatable=True,
+            ast_node=node,
+        )
+        assert directive.name == "test"
+        assert directive.locations == locations
+        assert directive.args == {"arg": arg}
+        assert directive.is_repeatable is True
+        assert directive.description == "test description"
+        assert directive.extensions is None
+        assert directive.ast_node is node
+
+    def defines_a_directive_with_no_args():
+        locations = [DirectiveLocation.QUERY]
+        directive = GraphQLDirective("Foo", locations=locations)
+
+        assert directive.name == "Foo"
+        assert directive.args == {}
+        assert directive.is_repeatable is False
+        assert directive.extensions is None
+        assert directive.locations == locations
+
+    def defines_a_directive_with_multiple_args():
+        args = {
+            "foo": GraphQLArgument(GraphQLString),
+            "bar": GraphQLArgument(GraphQLInt),
+        }
+        locations = [DirectiveLocation.QUERY]
+        directive = GraphQLDirective("Foo", locations=locations, args=args)
+
+        assert directive.name == "Foo"
+        assert directive.args == args
+        assert directive.is_repeatable is False
+        assert directive.locations == locations
+
+    def defines_a_repeatable_directive():
+        locations = [DirectiveLocation.QUERY]
+        directive = GraphQLDirective("Foo", is_repeatable=True, locations=locations)
+
+        assert directive.name == "Foo"
+        assert directive.args == {}
+        assert directive.is_repeatable is True
+        assert directive.locations == locations
+
+    def directive_accepts_input_types_as_arguments():
+        # noinspection PyTypeChecker
+        directive = GraphQLDirective(
+            name="Foo", locations=[], args={"arg": GraphQLString}  # type: ignore
+        )
+        arg = directive.args["arg"]
+        assert isinstance(arg, GraphQLArgument)
+        assert arg.type is GraphQLString
+
+    def directive_accepts_strings_as_locations():
+        # noinspection PyTypeChecker
+        directive = GraphQLDirective(
+            name="Foo", locations=["SCHEMA", "OBJECT"]  # type: ignore
+        )
+        assert directive.locations == [
+            DirectiveLocation.SCHEMA,
+            DirectiveLocation.OBJECT,
+        ]
+
+    def directive_has_str():
+        directive = GraphQLDirective("foo", [])
+        assert str(directive) == "@foo"
+
+    def directive_has_repr():
+        directive = GraphQLDirective("foo", [])
+        assert repr(directive) == "<GraphQLDirective(@foo)>"
+
+    def can_compare_with_other_source_directive():
+        locations = [DirectiveLocation.QUERY]
+        directive = GraphQLDirective("Foo", locations)
+        assert directive == directive
+        assert not directive != directive
+        assert not directive == {}
+        assert directive != {}
+        same_directive = GraphQLDirective("Foo", locations)
+        assert directive == same_directive
+        assert not directive != same_directive
+        other_directive = GraphQLDirective("Bar", locations)
+        assert not directive == other_directive
+        assert directive != other_directive
+        other_locations = [DirectiveLocation.MUTATION]
+        other_directive = GraphQLDirective("Foo", other_locations)
+        assert not directive == other_directive
+        assert directive != other_directive
+        other_directive = GraphQLDirective("Foo", locations, is_repeatable=True)
+        assert not directive == other_directive
+        assert directive != other_directive
+        other_directive = GraphQLDirective("Foo", locations, description="other")
+        assert not directive == other_directive
+        assert directive != other_directive
+
+    def rejects_an_unnamed_directive():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective(None, locations=[])  # type: ignore
+        assert str(exc_info.value) == "Directive must be named."
+
+    def rejects_a_directive_with_incorrectly_typed_name():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective({"bad": True}, locations=[])  # type: ignore
+        assert str(exc_info.value) == "The directive name must be a string."
+
+    def rejects_a_directive_with_incorrectly_typed_args():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective("Foo", locations=[], args=["arg"])  # type: ignore
+        assert str(exc_info.value) == (
+            "Foo args must be a dict with argument names as keys."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective(
+                "Foo",
+                locations=[],
+                args={1: GraphQLArgument(GraphQLString)},  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "Foo args must be a dict with argument names as keys."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective(
+                "Foo",
+                locations=[],
+                args={"arg": GraphQLDirective("Bar", [])},  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "Foo args must be GraphQLArgument or input type objects."
+        )
+
+    def rejects_a_directive_with_incorrectly_typed_repeatable_flag():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective("Foo", locations=[], is_repeatable=None)  # type: ignore
+        assert str(exc_info.value) == "Foo is_repeatable flag must be True or False."
+
+    def rejects_a_directive_with_undefined_locations():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective("Foo", locations=None)  # type: ignore
+        assert str(exc_info.value) == (
+            "Foo locations must be specified"
+            " as a collection of DirectiveLocation enum values."
+        )
+
+    def rejects_a_directive_with_incorrectly_typed_locations():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective("Foo", locations="bad")  # type: ignore
+        assert (
+            str(exc_info.value) == "Foo locations must be specified"
+            " as a collection of DirectiveLocation enum values."
+        )
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective("Foo", locations=["bad"])  # type: ignore
+        assert str(exc_info.value) == (
+            "Foo locations must be specified"
+            " as a collection of DirectiveLocation enum values."
+        )
+
+    def rejects_a_directive_with_incorrectly_typed_description():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective(
+                "Foo", locations=[], description={"bad": True}  # type: ignore
+            )
+        assert str(exc_info.value) == "Foo description must be a string."
+
+    def rejects_a_directive_with_incorrectly_typed_ast_node():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLDirective("Foo", locations=[], ast_node=Node())  # type: ignore
+        assert str(exc_info.value) == (
+            "Foo AST node must be a DirectiveDefinitionNode."
+        )
diff --git a/tests/type/test_enum.py b/tests/type/test_enum.py
new file mode 100644
index 0000000..5b0ef6c
--- /dev/null
+++ b/tests/type/test_enum.py
@@ -0,0 +1,345 @@
+from datetime import datetime
+from enum import Enum
+
+from graphql import graphql_sync
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInt,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+from graphql.utilities import introspection_from_schema
+
+ColorType = GraphQLEnumType("Color", values={"RED": 0, "GREEN": 1, "BLUE": 2})
+
+
+class ColorTypeEnumValues(Enum):
+    RED = 0
+    GREEN = 1
+    BLUE = 2
+
+
+class Complex1:
+    # noinspection PyMethodMayBeStatic
+    some_random_object = datetime.now()
+
+
+class Complex2:
+    some_random_value = 123
+
+
+complex1 = Complex1()
+complex2 = Complex2()
+
+ComplexEnum = GraphQLEnumType("Complex", {"ONE": complex1, "TWO": complex2})
+
+ColorType2 = GraphQLEnumType("Color", ColorTypeEnumValues)
+
+QueryType = GraphQLObjectType(
+    "Query",
+    {
+        "colorEnum": GraphQLField(
+            ColorType,
+            args={
+                "fromEnum": GraphQLArgument(ColorType),
+                "fromInt": GraphQLArgument(GraphQLInt),
+                "fromString": GraphQLArgument(GraphQLString),
+            },
+            resolve=lambda _source, info, **args: args.get("fromInt")
+            or args.get("fromString")
+            or args.get("fromEnum"),
+        ),
+        "colorInt": GraphQLField(
+            GraphQLInt,
+            args={
+                "fromEnum": GraphQLArgument(ColorType),
+                "fromInt": GraphQLArgument(GraphQLInt),
+            },
+            resolve=lambda _source, info, **args: args.get("fromEnum"),
+        ),
+        "complexEnum": GraphQLField(
+            ComplexEnum,
+            args={
+                # Note: default_value is provided an *internal* representation for
+                # Enums, rather than the string name.
+                "fromEnum": GraphQLArgument(ComplexEnum, default_value=complex1),
+                "provideGoodValue": GraphQLArgument(GraphQLBoolean),
+                "provideBadValue": GraphQLArgument(GraphQLBoolean),
+            },
+            resolve=lambda _source, info, **args:
+            # Note: this is one of the references of the internal values
+            # which ComplexEnum allows.
+            complex2 if args.get("provideGoodValue")
+            # Note: similar object, but not the same *reference* as
+            # complex2 above. Enum internal values require object equality.
+            else Complex2() if args.get("provideBadValue") else args.get("fromEnum"),
+        ),
+    },
+)
+
+MutationType = GraphQLObjectType(
+    "Mutation",
+    {
+        "favoriteEnum": GraphQLField(
+            ColorType,
+            args={"color": GraphQLArgument(ColorType)},
+            resolve=lambda _source, info, color=None: color,
+        )
+    },
+)
+
+SubscriptionType = GraphQLObjectType(
+    "Subscription",
+    {
+        "subscribeToEnum": GraphQLField(
+            ColorType,
+            args={"color": GraphQLArgument(ColorType)},
+            resolve=lambda _source, info, color=None: color,
+        )
+    },
+)
+
+schema = GraphQLSchema(
+    query=QueryType, mutation=MutationType, subscription=SubscriptionType
+)
+
+
+def execute_query(source, variable_values=None):
+    return graphql_sync(schema, source, variable_values=variable_values)
+
+
+def describe_type_system_enum_values():
+    def can_use_python_enums_instead_of_dicts():
+        assert ColorType2.values == ColorType.values
+        keys = [key for key in ColorType.values]
+        keys2 = [key for key in ColorType2.values]
+        assert keys2 == keys
+        values = [value.value for value in ColorType.values.values()]
+        values2 = [value.value for value in ColorType2.values.values()]
+        assert values2 == values
+
+    def accepts_enum_literals_as_input():
+        result = execute_query("{ colorInt(fromEnum: GREEN) }")
+
+        assert result == ({"colorInt": 1}, None)
+
+    def enum_may_be_output_type():
+        result = execute_query("{ colorEnum(fromInt: 1) }")
+
+        assert result == ({"colorEnum": "GREEN"}, None)
+
+    def enum_may_be_both_input_and_output_type():
+        result = execute_query("{ colorEnum(fromEnum: GREEN) }")
+
+        assert result == ({"colorEnum": "GREEN"}, None)
+
+    def does_not_accept_string_literals():
+        result = execute_query('{ colorEnum(fromEnum: "GREEN") }')
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Enum 'Color' cannot represent non-enum value:"
+                    ' "GREEN".'
+                    " Did you mean the enum value 'GREEN'?",
+                    "locations": [(1, 23)],
+                }
+            ],
+        )
+
+    def does_not_accept_values_not_in_the_enum():
+        result = execute_query("{ colorEnum(fromEnum: GREENISH) }")
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Value 'GREENISH' does not exist in 'Color' enum."
+                    " Did you mean the enum value 'GREEN'?",
+                    "locations": [(1, 23)],
+                }
+            ],
+        )
+
+    def does_not_accept_values_with_incorrect_casing():
+        result = execute_query("{ colorEnum(fromEnum: green) }")
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Value 'green' does not exist in 'Color' enum."
+                    " Did you mean the enum value 'GREEN' or 'RED'?",
+                    "locations": [(1, 23)],
+                }
+            ],
+        )
+
+    def does_not_accept_incorrect_internal_value():
+        result = execute_query('{ colorEnum(fromString: "GREEN") }')
+
+        assert result == (
+            {"colorEnum": None},
+            [
+                {
+                    "message": "Enum 'Color' cannot represent value: 'GREEN'",
+                    "locations": [(1, 3)],
+                    "path": ["colorEnum"],
+                }
+            ],
+        )
+
+    def does_not_accept_internal_value_in_place_of_enum_literal():
+        result = execute_query("{ colorEnum(fromEnum: 1) }")
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Enum 'Color' cannot represent non-enum value: 1.",
+                    "locations": [(1, 23)],
+                }
+            ],
+        )
+
+    def does_not_accept_enum_literal_in_place_of_int():
+        result = execute_query("{ colorEnum(fromInt: GREEN) }")
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Int cannot represent non-integer value: GREEN",
+                    "locations": [(1, 22)],
+                }
+            ],
+        )
+
+    def accepts_json_string_as_enum_variable():
+        doc = "query ($color: Color!) { colorEnum(fromEnum: $color) }"
+        result = execute_query(doc, {"color": "BLUE"})
+
+        assert result == ({"colorEnum": "BLUE"}, None)
+
+    def accepts_enum_literals_as_input_arguments_to_mutations():
+        doc = "mutation ($color: Color!) { favoriteEnum(color: $color) }"
+        result = execute_query(doc, {"color": "GREEN"})
+
+        assert result == ({"favoriteEnum": "GREEN"}, None)
+
+    def accepts_enum_literals_as_input_arguments_to_subscriptions():
+        doc = "subscription ($color: Color!) { subscribeToEnum(color: $color) }"
+        result = execute_query(doc, {"color": "GREEN"})
+
+        assert result == ({"subscribeToEnum": "GREEN"}, None)
+
+    def does_not_accept_internal_value_as_enum_variable():
+        doc = "query ($color: Color!) { colorEnum(fromEnum: $color) }"
+        result = execute_query(doc, {"color": 2})
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Variable '$color' got invalid value 2;"
+                    " Enum 'Color' cannot represent non-string value: 2.",
+                    "locations": [(1, 8)],
+                }
+            ],
+        )
+
+    def does_not_accept_string_variables_as_enum_input():
+        doc = "query ($color: String!) { colorEnum(fromEnum: $color) }"
+        result = execute_query(doc, {"color": "BLUE"})
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Variable '$color' of type 'String!'"
+                    " used in position expecting type 'Color'.",
+                    "locations": [(1, 8), (1, 47)],
+                }
+            ],
+        )
+
+    def does_not_accept_internal_value_variable_as_enum_input():
+        doc = "query ($color: Int!) { colorEnum(fromEnum: $color) }"
+        result = execute_query(doc, {"color": 2})
+
+        assert result == (
+            None,
+            [
+                {
+                    "message": "Variable '$color' of type 'Int!'"
+                    " used in position expecting type 'Color'.",
+                    "locations": [(1, 8), (1, 44)],
+                }
+            ],
+        )
+
+    def enum_value_may_have_an_internal_value_of_0():
+        result = execute_query(
+            """
+            {
+              colorEnum(fromEnum: RED)
+              colorInt(fromEnum: RED)
+            }
+            """
+        )
+
+        assert result == ({"colorEnum": "RED", "colorInt": 0}, None)
+
+    def enum_inputs_may_be_nullable():
+        result = execute_query(
+            """
+            {
+              colorEnum
+              colorInt
+            }
+            """
+        )
+
+        assert result == ({"colorEnum": None, "colorInt": None}, None)
+
+    def presents_a_values_property_for_complex_enums():
+        values = ComplexEnum.values
+        assert isinstance(values, dict)
+        assert all(isinstance(value, GraphQLEnumValue) for value in values.values())
+        assert {key: value.value for key, value in values.items()} == {
+            "ONE": complex1,
+            "TWO": complex2,
+        }
+
+    def may_be_internally_represented_with_complex_values():
+        result = execute_query(
+            """
+            {
+              first: complexEnum
+              second: complexEnum(fromEnum: TWO)
+              good: complexEnum(provideGoodValue: true)
+              bad: complexEnum(provideBadValue: true)
+            }
+            """
+        )
+
+        assert result == (
+            {"first": "ONE", "second": "TWO", "good": "TWO", "bad": None},
+            [
+                {
+                    "message": "Enum 'Complex' cannot represent value:"
+                    " <Complex2 instance>",
+                    "locations": [(6, 15)],
+                    "path": ["bad"],
+                }
+            ],
+        )
+
+    def can_be_introspected_without_error():
+        introspection_from_schema(schema)
diff --git a/tests/type/test_extensions.py b/tests/type/test_extensions.py
new file mode 100644
index 0000000..34053e8
--- /dev/null
+++ b/tests/type/test_extensions.py
@@ -0,0 +1,335 @@
+from typing import Any, Dict, cast
+
+from pytest import mark, param, raises  # type: ignore
+
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLDirective,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInterfaceType,
+    GraphQLObjectType,
+    GraphQLScalarType,
+    GraphQLSchema,
+    GraphQLUnionType,
+)
+
+dummy_type = GraphQLScalarType("DummyScalar")
+
+bad_extensions = [param([], id="list"), param({1: "ext"}, id="non_string_key")]
+
+
+def bad_extensions_msg(name: str) -> str:
+    return f"{name} extensions must be a dictionary with string keys."
+
+
+def describe_type_system_extensions():
+    def describe_graphql_scalar_type():
+        def without_extensions():
+            some_scalar = GraphQLScalarType("SomeScalar")
+            assert some_scalar.extensions is None
+            assert some_scalar.to_kwargs()["extensions"] is None
+
+        def with_extensions():
+            scalar_extensions = {"SomeScalarExt": "scalar"}
+            some_scalar = GraphQLScalarType("SomeScalar", extensions=scalar_extensions)
+
+            assert some_scalar.extensions is scalar_extensions
+            assert some_scalar.to_kwargs()["extensions"] is scalar_extensions
+
+        @mark.parametrize("extensions", bad_extensions)
+        def with_bad_extensions(extensions):
+            with raises(TypeError, match=bad_extensions_msg("SomeScalar")):
+                # noinspection PyTypeChecker
+                GraphQLScalarType("SomeScalar", extensions=extensions)
+
+    def describe_graphql_object_type():
+        def without_extensions():
+            some_object = GraphQLObjectType(
+                "SomeObject",
+                {
+                    "someField": GraphQLField(
+                        dummy_type, {"someArg": GraphQLArgument(dummy_type)}
+                    )
+                },
+            )
+
+            assert some_object.extensions is None
+            some_field = some_object.fields["someField"]
+            assert some_field.extensions is None
+            some_arg = some_field.args["someArg"]
+            assert some_arg.extensions is None
+
+            assert some_object.to_kwargs()["extensions"] is None
+            assert some_field.to_kwargs()["extensions"] is None
+            assert some_arg.to_kwargs()["extensions"] is None
+
+        def with_extensions():
+            object_extensions = {"SomeObjectExt": "object"}
+            field_extensions = {"SomeFieldExt": "field"}
+            arg_extensions = {"SomeArgExt": "arg"}
+
+            some_object = GraphQLObjectType(
+                "SomeObject",
+                {
+                    "someField": GraphQLField(
+                        dummy_type,
+                        {
+                            "someArg": GraphQLArgument(
+                                dummy_type, extensions=arg_extensions
+                            )
+                        },
+                        extensions=field_extensions,
+                    )
+                },
+                extensions=object_extensions,
+            )
+
+            assert some_object.extensions is object_extensions
+            some_field = some_object.fields["someField"]
+            assert some_field.extensions is field_extensions
+            some_arg = some_field.args["someArg"]
+            assert some_arg.extensions is arg_extensions
+
+            assert some_object.to_kwargs()["extensions"] is object_extensions
+            assert some_field.to_kwargs()["extensions"] is field_extensions
+            assert some_arg.to_kwargs()["extensions"] is arg_extensions
+
+        @mark.parametrize("extensions", bad_extensions)
+        def with_bad_extensions(extensions):
+            with raises(TypeError, match=bad_extensions_msg("SomeObject")):
+                # noinspection PyTypeChecker
+                GraphQLObjectType("SomeObject", {}, extensions=extensions)
+            with raises(TypeError, match=bad_extensions_msg("Field")):
+                # noinspection PyTypeChecker
+                GraphQLField(dummy_type, extensions=extensions)
+            with raises(TypeError, match=bad_extensions_msg("Argument")):
+                # noinspection PyTypeChecker
+                GraphQLArgument(dummy_type, extensions=extensions)
+
+    def describe_graphql_interface_type():
+        def without_extensions():
+            some_interface = GraphQLInterfaceType(
+                "SomeInterface",
+                {
+                    "someField": GraphQLField(
+                        dummy_type, {"someArg": GraphQLArgument(dummy_type)}
+                    )
+                },
+            )
+
+            assert some_interface.extensions is None
+            some_field = some_interface.fields["someField"]
+            assert some_field.extensions is None
+            some_arg = some_field.args["someArg"]
+            assert some_arg.extensions is None
+
+            assert some_interface.to_kwargs()["extensions"] is None
+            assert some_field.to_kwargs()["extensions"] is None
+            assert some_arg.to_kwargs()["extensions"] is None
+
+        def with_extensions():
+            interface_extensions = {"SomeInterfaceExt": "interface"}
+            field_extensions = {"SomeFieldExt": "field"}
+            arg_extensions = {"SomeArgExt": "arg"}
+
+            some_interface = GraphQLInterfaceType(
+                "SomeInterface",
+                {
+                    "someField": GraphQLField(
+                        dummy_type,
+                        {
+                            "someArg": GraphQLArgument(
+                                dummy_type, extensions=arg_extensions
+                            )
+                        },
+                        extensions=field_extensions,
+                    )
+                },
+                extensions=interface_extensions,
+            )
+
+            assert some_interface.extensions is interface_extensions
+            some_field = some_interface.fields["someField"]
+            assert some_field.extensions is field_extensions
+            some_arg = some_field.args["someArg"]
+            assert some_arg.extensions is arg_extensions
+
+            assert some_interface.to_kwargs()["extensions"] is interface_extensions
+            assert some_field.to_kwargs()["extensions"] is field_extensions
+            assert some_arg.to_kwargs()["extensions"] is arg_extensions
+
+        @mark.parametrize("extensions", bad_extensions)
+        def with_bad_extensions(extensions):
+            with raises(TypeError, match=bad_extensions_msg("SomeInterface")):
+                # noinspection PyTypeChecker
+                GraphQLInterfaceType("SomeInterface", {}, extensions=extensions)
+
+    def describe_graphql_union_type():
+        def without_extensions():
+            some_union = GraphQLUnionType("SomeUnion", [])
+
+            assert some_union.extensions is None
+
+            assert some_union.to_kwargs()["extensions"] is None
+
+        def with_extensions():
+            union_extensions = {"SomeUnionExt": "union"}
+
+            some_union = GraphQLUnionType("SomeUnion", [], extensions=union_extensions)
+
+            assert some_union.extensions is union_extensions
+
+            assert some_union.to_kwargs()["extensions"] is union_extensions
+
+        @mark.parametrize("extensions", bad_extensions)
+        def with_bad_extensions(extensions):
+            with raises(TypeError, match=bad_extensions_msg("SomeUnion")):
+                # noinspection PyTypeChecker
+                GraphQLUnionType("SomeUnion", [], extensions=extensions)
+
+    def describe_graphql_enum_type():
+        def without_extensions():
+            some_enum = GraphQLEnumType("SomeEnum", {"SOME_VALUE": None})
+
+            assert some_enum.extensions is None
+            some_value = some_enum.values["SOME_VALUE"]
+            assert some_value.extensions is None
+
+            assert some_enum.to_kwargs()["extensions"] is None
+            assert some_value.to_kwargs()["extensions"] is None
+
+        def with_extensions():
+            enum_extensions = {"SomeEnumExt": "enum"}
+            value_extensions = {"SomeValueExt": "value"}
+
+            some_enum = GraphQLEnumType(
+                "SomeEnum",
+                {"SOME_VALUE": GraphQLEnumValue(extensions=value_extensions)},
+                extensions=enum_extensions,
+            )
+
+            assert some_enum.extensions is enum_extensions
+            some_value = some_enum.values["SOME_VALUE"]
+            assert some_value.extensions is value_extensions
+
+            assert some_enum.to_kwargs()["extensions"] is enum_extensions
+            assert some_value.to_kwargs()["extensions"] is value_extensions
+
+        @mark.parametrize("extensions", bad_extensions)
+        def with_bad_extensions(extensions):
+            with raises(TypeError, match=bad_extensions_msg("SomeEnum")):
+                # noinspection PyTypeChecker
+                GraphQLEnumType(
+                    "SomeEnum", cast(Dict[str, Any], {}), extensions=extensions
+                )
+            with raises(TypeError, match=bad_extensions_msg("Enum value")):
+                # noinspection PyTypeChecker
+                GraphQLEnumValue(extensions=extensions)
+
+    def describe_graphql_input_object_type():
+        def without_extensions():
+            some_input_object = GraphQLInputObjectType(
+                "SomeInputObject", {"someInputField": GraphQLInputField(dummy_type)}
+            )
+
+            assert some_input_object.extensions is None
+            some_input_field = some_input_object.fields["someInputField"]
+            assert some_input_field.extensions is None
+
+            assert some_input_object.to_kwargs()["extensions"] is None
+            assert some_input_field.to_kwargs()["extensions"] is None
+
+        def with_extensions():
+            input_object_extensions = {"SomeInputObjectExt": "inputObject"}
+            input_field_extensions = {"SomeInputFieldExt": "inputField"}
+
+            some_input_object = GraphQLInputObjectType(
+                "SomeInputObject",
+                {
+                    "someInputField": GraphQLInputField(
+                        dummy_type, extensions=input_field_extensions
+                    )
+                },
+                extensions=input_object_extensions,
+            )
+
+            assert some_input_object.extensions is input_object_extensions
+            some_input_field = some_input_object.fields["someInputField"]
+            assert some_input_field.extensions is input_field_extensions
+
+            assert (
+                some_input_object.to_kwargs()["extensions"] is input_object_extensions
+            )
+            assert some_input_field.to_kwargs()["extensions"] is input_field_extensions
+
+        @mark.parametrize("extensions", bad_extensions)
+        def with_bad_extensions(extensions):
+            with raises(TypeError, match=bad_extensions_msg("SomeInputObject")):
+                # noinspection PyTypeChecker
+                GraphQLInputObjectType("SomeInputObject", {}, extensions=extensions)
+            with raises(TypeError, match=bad_extensions_msg("Input field")):
+                # noinspection PyTypeChecker
+                GraphQLInputField(dummy_type, extensions=extensions)
+
+    def describe_graphql_directive():
+        def without_extensions():
+            some_directive = GraphQLDirective(
+                "SomeDirective", [], {"someArg": GraphQLArgument(dummy_type)}
+            )
+
+            assert some_directive.extensions is None
+            some_arg = some_directive.args["someArg"]
+            assert some_arg.extensions is None
+
+            assert some_directive.to_kwargs()["extensions"] is None
+            assert some_arg.to_kwargs()["extensions"] is None
+
+        def with_extensions():
+            directive_extensions = {"SomeDirectiveExt": "directive"}
+            arg_extensions = {"SomeArgExt": "arg"}
+
+            some_directive = GraphQLDirective(
+                "SomeDirective",
+                [],
+                {"someArg": GraphQLArgument(dummy_type, extensions=arg_extensions)},
+                extensions=directive_extensions,
+            )
+
+            assert some_directive.extensions is directive_extensions
+            some_arg = some_directive.args["someArg"]
+            assert some_arg.extensions is arg_extensions
+
+            assert some_directive.to_kwargs()["extensions"] is directive_extensions
+            assert some_arg.to_kwargs()["extensions"] is arg_extensions
+
+        @mark.parametrize("extensions", bad_extensions)
+        def with_bad_extensions(extensions):
+            with raises(TypeError, match=bad_extensions_msg("Directive")):
+                # noinspection PyTypeChecker
+                GraphQLDirective("SomeDirective", [], extensions=extensions)
+
+    def describe_graphql_schema():
+        def without_extensions():
+            schema = GraphQLSchema()
+
+            assert schema.extensions is None
+            assert schema.to_kwargs()["extensions"] is None
+
+        def with_extensions():
+            schema_extensions = {"schemaExtension": "schema"}
+
+            schema = GraphQLSchema(extensions=schema_extensions)
+
+            assert schema.extensions is schema_extensions
+
+            assert schema.to_kwargs()["extensions"] is schema_extensions
+
+        @mark.parametrize("extensions", bad_extensions)
+        def with_bad_extensions(extensions):
+            with raises(TypeError, match=bad_extensions_msg("Schema")):
+                # noinspection PyTypeChecker
+                GraphQLSchema(extensions=extensions)
diff --git a/tests/type/test_introspection.py b/tests/type/test_introspection.py
new file mode 100644
index 0000000..98891b0
--- /dev/null
+++ b/tests/type/test_introspection.py
@@ -0,0 +1,1412 @@
+from graphql import graphql_sync
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLList,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+)
+from graphql.utilities import get_introspection_query
+
+
+def describe_introspection():
+    def executes_an_introspection_query():
+        schema = GraphQLSchema(
+            GraphQLObjectType("QueryRoot", {"onlyField": GraphQLField(GraphQLString)}),
+            description="Sample schema",
+        )
+        source = get_introspection_query(
+            descriptions=False, specified_by_url=True, directive_is_repeatable=True
+        )
+
+        result = graphql_sync(schema=schema, source=source)
+        assert result.errors is None
+        assert result.data == {
+            "__schema": {
+                "mutationType": None,
+                "subscriptionType": None,
+                "queryType": {"name": "QueryRoot"},
+                "types": [
+                    {
+                        "kind": "OBJECT",
+                        "name": "QueryRoot",
+                        "specifiedByUrl": None,
+                        "fields": [
+                            {
+                                "name": "onlyField",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            }
+                        ],
+                        "inputFields": None,
+                        "interfaces": [],
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "SCALAR",
+                        "name": "String",
+                        "specifiedByUrl": None,
+                        "fields": None,
+                        "inputFields": None,
+                        "interfaces": None,
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "SCALAR",
+                        "name": "Boolean",
+                        "specifiedByUrl": None,
+                        "fields": None,
+                        "inputFields": None,
+                        "interfaces": None,
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "OBJECT",
+                        "name": "__Schema",
+                        "specifiedByUrl": None,
+                        "fields": [
+                            {
+                                "name": "description",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "types",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "LIST",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "NON_NULL",
+                                            "name": None,
+                                            "ofType": {
+                                                "kind": "OBJECT",
+                                                "name": "__Type",
+                                                "ofType": None,
+                                            },
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "queryType",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "OBJECT",
+                                        "name": "__Type",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "mutationType",
+                                "args": [],
+                                "type": {
+                                    "kind": "OBJECT",
+                                    "name": "__Type",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "subscriptionType",
+                                "args": [],
+                                "type": {
+                                    "kind": "OBJECT",
+                                    "name": "__Type",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "directives",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "LIST",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "NON_NULL",
+                                            "name": None,
+                                            "ofType": {
+                                                "kind": "OBJECT",
+                                                "name": "__Directive",
+                                                "ofType": None,
+                                            },
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                        ],
+                        "inputFields": None,
+                        "interfaces": [],
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "OBJECT",
+                        "name": "__Type",
+                        "specifiedByUrl": None,
+                        "fields": [
+                            {
+                                "name": "kind",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "ENUM",
+                                        "name": "__TypeKind",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "name",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "description",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "specifiedByUrl",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "fields",
+                                "args": [
+                                    {
+                                        "name": "includeDeprecated",
+                                        "type": {
+                                            "kind": "SCALAR",
+                                            "name": "Boolean",
+                                            "ofType": None,
+                                        },
+                                        "defaultValue": "false",
+                                    }
+                                ],
+                                "type": {
+                                    "kind": "LIST",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "NON_NULL",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "OBJECT",
+                                            "name": "__Field",
+                                            "ofType": None,
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "interfaces",
+                                "args": [],
+                                "type": {
+                                    "kind": "LIST",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "NON_NULL",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "OBJECT",
+                                            "name": "__Type",
+                                            "ofType": None,
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "possibleTypes",
+                                "args": [],
+                                "type": {
+                                    "kind": "LIST",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "NON_NULL",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "OBJECT",
+                                            "name": "__Type",
+                                            "ofType": None,
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "enumValues",
+                                "args": [
+                                    {
+                                        "name": "includeDeprecated",
+                                        "type": {
+                                            "kind": "SCALAR",
+                                            "name": "Boolean",
+                                            "ofType": None,
+                                        },
+                                        "defaultValue": "false",
+                                    }
+                                ],
+                                "type": {
+                                    "kind": "LIST",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "NON_NULL",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "OBJECT",
+                                            "name": "__EnumValue",
+                                            "ofType": None,
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "inputFields",
+                                "args": [],
+                                "type": {
+                                    "kind": "LIST",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "NON_NULL",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "OBJECT",
+                                            "name": "__InputValue",
+                                            "ofType": None,
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "ofType",
+                                "args": [],
+                                "type": {
+                                    "kind": "OBJECT",
+                                    "name": "__Type",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                        ],
+                        "inputFields": None,
+                        "interfaces": [],
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "ENUM",
+                        "name": "__TypeKind",
+                        "specifiedByUrl": None,
+                        "fields": None,
+                        "inputFields": None,
+                        "interfaces": None,
+                        "enumValues": [
+                            {
+                                "name": "SCALAR",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "OBJECT",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "INTERFACE",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "UNION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "ENUM",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "INPUT_OBJECT",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "LIST",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "NON_NULL",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                        ],
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "OBJECT",
+                        "name": "__Field",
+                        "specifiedByUrl": None,
+                        "fields": [
+                            {
+                                "name": "name",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "String",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "description",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "args",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "LIST",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "NON_NULL",
+                                            "name": None,
+                                            "ofType": {
+                                                "kind": "OBJECT",
+                                                "name": "__InputValue",
+                                                "ofType": None,
+                                            },
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "type",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "OBJECT",
+                                        "name": "__Type",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "isDeprecated",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "Boolean",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "deprecationReason",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                        ],
+                        "inputFields": None,
+                        "interfaces": [],
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "OBJECT",
+                        "name": "__InputValue",
+                        "specifiedByUrl": None,
+                        "fields": [
+                            {
+                                "name": "name",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "String",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "description",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "type",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "OBJECT",
+                                        "name": "__Type",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "defaultValue",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                        ],
+                        "inputFields": None,
+                        "interfaces": [],
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "OBJECT",
+                        "name": "__EnumValue",
+                        "specifiedByUrl": None,
+                        "fields": [
+                            {
+                                "name": "name",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "String",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "description",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "isDeprecated",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "Boolean",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "deprecationReason",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                        ],
+                        "inputFields": None,
+                        "interfaces": [],
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "OBJECT",
+                        "name": "__Directive",
+                        "specifiedByUrl": None,
+                        "fields": [
+                            {
+                                "name": "name",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "String",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "description",
+                                "args": [],
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "isRepeatable",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "Boolean",
+                                        "ofType": None,
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "locations",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "LIST",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "NON_NULL",
+                                            "name": None,
+                                            "ofType": {
+                                                "kind": "ENUM",
+                                                "name": "__DirectiveLocation",
+                                                "ofType": None,
+                                            },
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "args",
+                                "args": [],
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "LIST",
+                                        "name": None,
+                                        "ofType": {
+                                            "kind": "NON_NULL",
+                                            "name": None,
+                                            "ofType": {
+                                                "kind": "OBJECT",
+                                                "name": "__InputValue",
+                                                "ofType": None,
+                                            },
+                                        },
+                                    },
+                                },
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                        ],
+                        "inputFields": None,
+                        "interfaces": [],
+                        "enumValues": None,
+                        "possibleTypes": None,
+                    },
+                    {
+                        "kind": "ENUM",
+                        "name": "__DirectiveLocation",
+                        "specifiedByUrl": None,
+                        "fields": None,
+                        "inputFields": None,
+                        "interfaces": None,
+                        "enumValues": [
+                            {
+                                "name": "QUERY",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "MUTATION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "SUBSCRIPTION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "FIELD",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "FRAGMENT_DEFINITION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "FRAGMENT_SPREAD",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "INLINE_FRAGMENT",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "VARIABLE_DEFINITION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "SCHEMA",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "SCALAR",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "OBJECT",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "FIELD_DEFINITION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "ARGUMENT_DEFINITION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "INTERFACE",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "UNION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "ENUM",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "ENUM_VALUE",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "INPUT_OBJECT",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                            {
+                                "name": "INPUT_FIELD_DEFINITION",
+                                "isDeprecated": False,
+                                "deprecationReason": None,
+                            },
+                        ],
+                        "possibleTypes": None,
+                    },
+                ],
+                "directives": [
+                    {
+                        "name": "include",
+                        "isRepeatable": False,
+                        "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
+                        "args": [
+                            {
+                                "defaultValue": None,
+                                "name": "if",
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "Boolean",
+                                        "ofType": None,
+                                    },
+                                },
+                            }
+                        ],
+                    },
+                    {
+                        "name": "skip",
+                        "isRepeatable": False,
+                        "locations": ["FIELD", "FRAGMENT_SPREAD", "INLINE_FRAGMENT"],
+                        "args": [
+                            {
+                                "defaultValue": None,
+                                "name": "if",
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "Boolean",
+                                        "ofType": None,
+                                    },
+                                },
+                            }
+                        ],
+                    },
+                    {
+                        "name": "deprecated",
+                        "isRepeatable": False,
+                        "locations": ["FIELD_DEFINITION", "ENUM_VALUE"],
+                        "args": [
+                            {
+                                "defaultValue": '"No longer supported"',
+                                "name": "reason",
+                                "type": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                            }
+                        ],
+                    },
+                    {
+                        "name": "specifiedBy",
+                        "isRepeatable": False,
+                        "locations": ["SCALAR"],
+                        "args": [
+                            {
+                                "defaultValue": None,
+                                "name": "url",
+                                "type": {
+                                    "kind": "NON_NULL",
+                                    "name": None,
+                                    "ofType": {
+                                        "kind": "SCALAR",
+                                        "name": "String",
+                                        "ofType": None,
+                                    },
+                                },
+                            }
+                        ],
+                    },
+                ],
+            }
+        }
+
+    def introspects_on_input_object():
+        TestInputObject = GraphQLInputObjectType(
+            "TestInputObject",
+            {
+                "a": GraphQLInputField(GraphQLString, default_value="tes\t de\fault"),
+                "b": GraphQLInputField(GraphQLList(GraphQLString)),
+                "c": GraphQLInputField(GraphQLString, default_value=None),
+            },
+        )
+
+        TestType = GraphQLObjectType(
+            "TestType",
+            {
+                "field": GraphQLField(
+                    GraphQLString, args={"complex": GraphQLArgument(TestInputObject)}
+                )
+            },
+        )
+
+        schema = GraphQLSchema(TestType)
+        source = """
+            {
+              __type(name: "TestInputObject") {
+                kind
+                name
+                inputFields {
+                  name
+                  type { ...TypeRef }
+                  defaultValue
+                }
+              }
+            }
+
+            fragment TypeRef on __Type {
+              kind
+              name
+              ofType {
+                kind
+                name
+                ofType {
+                  kind
+                  name
+                  ofType {
+                    kind
+                    name
+                  }
+                }
+              }
+            }
+            """
+
+        assert graphql_sync(schema=schema, source=source) == (
+            {
+                "__type": {
+                    "kind": "INPUT_OBJECT",
+                    "name": "TestInputObject",
+                    "inputFields": [
+                        {
+                            "name": "a",
+                            "type": {
+                                "kind": "SCALAR",
+                                "name": "String",
+                                "ofType": None,
+                            },
+                            "defaultValue": '"tes\\t de\\fault"',
+                        },
+                        {
+                            "name": "b",
+                            "type": {
+                                "kind": "LIST",
+                                "name": None,
+                                "ofType": {
+                                    "kind": "SCALAR",
+                                    "name": "String",
+                                    "ofType": None,
+                                },
+                            },
+                            "defaultValue": None,
+                        },
+                        {
+                            "name": "c",
+                            "type": {
+                                "kind": "SCALAR",
+                                "name": "String",
+                                "ofType": None,
+                            },
+                            "defaultValue": "null",
+                        },
+                    ],
+                }
+            },
+            None,
+        )
+
+    def supports_the_type_root_field():
+        TestType = GraphQLObjectType(
+            "TestType", {"testField": GraphQLField(GraphQLString)}
+        )
+
+        schema = GraphQLSchema(TestType)
+        source = """
+            {
+              __type(name: "TestType") {
+                name
+              }
+            }
+            """
+
+        assert graphql_sync(schema=schema, source=source) == (
+            {"__type": {"name": "TestType"}},
+            None,
+        )
+
+    def identifies_deprecated_fields():
+        TestType = GraphQLObjectType(
+            "TestType",
+            {
+                "nonDeprecated": GraphQLField(GraphQLString),
+                "deprecated": GraphQLField(
+                    GraphQLString, deprecation_reason="Removed in 1.0"
+                ),
+                "deprecatedWithEmptyReason": GraphQLField(
+                    GraphQLString, deprecation_reason=""
+                ),
+            },
+        )
+
+        schema = GraphQLSchema(TestType)
+        source = """
+            {
+              __type(name: "TestType") {
+                name
+                fields(includeDeprecated: true) {
+                  name
+                  isDeprecated,
+                  deprecationReason
+                }
+              }
+            }
+            """
+
+        assert graphql_sync(schema=schema, source=source) == (
+            {
+                "__type": {
+                    "name": "TestType",
+                    "fields": [
+                        {
+                            "name": "nonDeprecated",
+                            "isDeprecated": False,
+                            "deprecationReason": None,
+                        },
+                        {
+                            "name": "deprecated",
+                            "isDeprecated": True,
+                            "deprecationReason": "Removed in 1.0",
+                        },
+                        {
+                            "name": "deprecatedWithEmptyReason",
+                            "isDeprecated": True,
+                            "deprecationReason": "",
+                        },
+                    ],
+                }
+            },
+            None,
+        )
+
+    def respects_the_include_deprecated_parameter_for_fields():
+        TestType = GraphQLObjectType(
+            "TestType",
+            {
+                "nonDeprecated": GraphQLField(GraphQLString),
+                "deprecated": GraphQLField(
+                    GraphQLString, deprecation_reason="Removed in 1.0"
+                ),
+            },
+        )
+
+        schema = GraphQLSchema(TestType)
+        source = """
+            {
+              __type(name: "TestType") {
+                name
+                trueFields: fields(includeDeprecated: true) {
+                  name
+                }
+                falseFields: fields(includeDeprecated: false) {
+                  name
+                }
+                omittedFields: fields {
+                  name
+                }
+              }
+            }
+            """
+
+        assert graphql_sync(schema=schema, source=source) == (
+            {
+                "__type": {
+                    "name": "TestType",
+                    "trueFields": [{"name": "nonDeprecated"}, {"name": "deprecated"}],
+                    "falseFields": [{"name": "nonDeprecated"}],
+                    "omittedFields": [{"name": "nonDeprecated"}],
+                }
+            },
+            None,
+        )
+
+    def identifies_deprecated_enum_values():
+        TestEnum = GraphQLEnumType(
+            "TestEnum",
+            {
+                "NON_DEPRECATED": GraphQLEnumValue(0),
+                "DEPRECATED": GraphQLEnumValue(1, deprecation_reason="Removed in 1.0"),
+                "ALSO_NON_DEPRECATED": GraphQLEnumValue(2),
+            },
+        )
+
+        TestType = GraphQLObjectType("TestType", {"testEnum": GraphQLField(TestEnum)})
+
+        schema = GraphQLSchema(TestType)
+        source = """
+            {
+              __type(name: "TestEnum") {
+                name
+                enumValues(includeDeprecated: true) {
+                  name
+                  isDeprecated,
+                  deprecationReason
+                }
+              }
+            }
+            """
+
+        assert graphql_sync(schema=schema, source=source) == (
+            {
+                "__type": {
+                    "name": "TestEnum",
+                    "enumValues": [
+                        {
+                            "name": "NON_DEPRECATED",
+                            "isDeprecated": False,
+                            "deprecationReason": None,
+                        },
+                        {
+                            "name": "DEPRECATED",
+                            "isDeprecated": True,
+                            "deprecationReason": "Removed in 1.0",
+                        },
+                        {
+                            "name": "ALSO_NON_DEPRECATED",
+                            "isDeprecated": False,
+                            "deprecationReason": None,
+                        },
+                    ],
+                }
+            },
+            None,
+        )
+
+    def respects_the_include_deprecated_parameter_for_enum_values():
+        TestEnum = GraphQLEnumType(
+            "TestEnum",
+            {
+                "NON_DEPRECATED": GraphQLEnumValue(0),
+                "DEPRECATED": GraphQLEnumValue(1, deprecation_reason="Removed in 1.0"),
+                "DEPRECATED_WITH_EMPTY_REASON": GraphQLEnumValue(
+                    2, deprecation_reason=""
+                ),
+                "ALSO_NON_DEPRECATED": GraphQLEnumValue(3),
+            },
+        )
+
+        TestType = GraphQLObjectType("TestType", {"testEnum": GraphQLField(TestEnum)})
+
+        schema = GraphQLSchema(TestType)
+        source = """
+            {
+              __type(name: "TestEnum") {
+                trueValues: enumValues(includeDeprecated: true) {
+                  name
+                }
+                falseValues: enumValues(includeDeprecated: false) {
+                  name
+                }
+                omittedValues: enumValues {
+                  name
+                }
+              }
+            }
+            """
+
+        assert graphql_sync(schema=schema, source=source) == (
+            {
+                "__type": {
+                    "trueValues": [
+                        {"name": "NON_DEPRECATED"},
+                        {"name": "DEPRECATED"},
+                        {"name": "DEPRECATED_WITH_EMPTY_REASON"},
+                        {"name": "ALSO_NON_DEPRECATED"},
+                    ],
+                    "falseValues": [
+                        {"name": "NON_DEPRECATED"},
+                        {"name": "ALSO_NON_DEPRECATED"},
+                    ],
+                    "omittedValues": [
+                        {"name": "NON_DEPRECATED"},
+                        {"name": "ALSO_NON_DEPRECATED"},
+                    ],
+                }
+            },
+            None,
+        )
+
+    def fails_as_expected_on_the_type_root_field_without_an_arg():
+        TestType = GraphQLObjectType(
+            "TestType", {"testField": GraphQLField(GraphQLString)}
+        )
+
+        schema = GraphQLSchema(TestType)
+        source = """
+            {
+              __type {
+                name
+              }
+            }
+            """
+        assert graphql_sync(schema=schema, source=source) == (
+            None,
+            [
+                {
+                    "message": "Field '__type' argument 'name'"
+                    " of type 'String!' is required, but it was not provided.",
+                    "locations": [(3, 15)],
+                }
+            ],
+        )
+
+    def exposes_descriptions_on_types_and_fields():
+        QueryRoot = GraphQLObjectType(
+            "QueryRoot", {"onlyField": GraphQLField(GraphQLString)}
+        )
+
+        schema = GraphQLSchema(QueryRoot)
+        source = """
+            {
+              schemaType: __type(name: "__Schema") {
+                name,
+                description,
+                fields {
+                  name,
+                  description
+                }
+              }
+            }
+            """
+
+        assert graphql_sync(schema=schema, source=source) == (
+            {
+                "schemaType": {
+                    "name": "__Schema",
+                    "description": "A GraphQL Schema defines the capabilities of a"
+                    " GraphQL server. It exposes all available types and"
+                    " directives on the server, as well as the entry points"
+                    " for query, mutation, and subscription operations.",
+                    "fields": [
+                        {"name": "description", "description": None},
+                        {
+                            "name": "types",
+                            "description": "A list of all types supported"
+                            " by this server.",
+                        },
+                        {
+                            "name": "queryType",
+                            "description": "The type that query operations"
+                            " will be rooted at.",
+                        },
+                        {
+                            "name": "mutationType",
+                            "description": "If this server supports mutation, the type"
+                            " that mutation operations will be rooted at.",
+                        },
+                        {
+                            "name": "subscriptionType",
+                            "description": "If this server support subscription,"
+                            " the type that subscription operations will be rooted at.",
+                        },
+                        {
+                            "name": "directives",
+                            "description": "A list of all directives supported"
+                            " by this server.",
+                        },
+                    ],
+                }
+            },
+            None,
+        )
+
+    def exposes_descriptions_on_enums():
+        QueryRoot = GraphQLObjectType(
+            "QueryRoot", {"onlyField": GraphQLField(GraphQLString)}
+        )
+
+        schema = GraphQLSchema(QueryRoot)
+        source = """
+            {
+              typeKindType: __type(name: "__TypeKind") {
+                name,
+                description,
+                enumValues {
+                  name,
+                  description
+                }
+              }
+            }
+            """
+
+        assert graphql_sync(schema=schema, source=source) == (
+            {
+                "typeKindType": {
+                    "name": "__TypeKind",
+                    "description": "An enum describing what kind of type"
+                    " a given `__Type` is.",
+                    "enumValues": [
+                        {
+                            "description": "Indicates this type is a scalar.",
+                            "name": "SCALAR",
+                        },
+                        {
+                            "description": "Indicates this type is an object."
+                            + " `fields` and `interfaces` are valid fields.",
+                            "name": "OBJECT",
+                        },
+                        {
+                            "description": "Indicates this type is an interface."
+                            " `fields`, `interfaces`, and `possibleTypes`"
+                            " are valid fields.",
+                            "name": "INTERFACE",
+                        },
+                        {
+                            "description": "Indicates this type is a union."
+                            " `possibleTypes` is a valid field.",
+                            "name": "UNION",
+                        },
+                        {
+                            "description": "Indicates this type is an enum."
+                            " `enumValues` is a valid field.",
+                            "name": "ENUM",
+                        },
+                        {
+                            "description": "Indicates this type is an input object."
+                            " `inputFields` is a valid field.",
+                            "name": "INPUT_OBJECT",
+                        },
+                        {
+                            "description": "Indicates this type is a list."
+                            " `ofType` is a valid field.",
+                            "name": "LIST",
+                        },
+                        {
+                            "description": "Indicates this type is a non-null."
+                            " `ofType` is a valid field.",
+                            "name": "NON_NULL",
+                        },
+                    ],
+                }
+            },
+            None,
+        )
+
+    def executes_introspection_query_without_calling_global_field_resolver():
+        query_root = GraphQLObjectType(
+            "QueryRoot", {"onlyField": GraphQLField(GraphQLString)}
+        )
+
+        schema = GraphQLSchema(query_root)
+        source = get_introspection_query(directive_is_repeatable=True)
+
+        def field_resolver(_obj, info):
+            assert False, f"Called on {info.parent_type.name}.{info.field_name}"
+
+        graphql_sync(schema=schema, source=source, field_resolver=field_resolver)
diff --git a/tests/type/test_predicate.py b/tests/type/test_predicate.py
new file mode 100644
index 0000000..5802cb2
--- /dev/null
+++ b/tests/type/test_predicate.py
@@ -0,0 +1,536 @@
+from pytest import raises  # type: ignore
+
+from graphql.language import DirectiveLocation
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLDeprecatedDirective,
+    GraphQLBoolean,
+    GraphQLDirective,
+    GraphQLEnumType,
+    GraphQLFloat,
+    GraphQLID,
+    GraphQLIncludeDirective,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLScalarType,
+    GraphQLSkipDirective,
+    GraphQLString,
+    GraphQLUnionType,
+    assert_abstract_type,
+    assert_composite_type,
+    assert_directive,
+    assert_enum_type,
+    assert_input_object_type,
+    assert_input_type,
+    assert_interface_type,
+    assert_leaf_type,
+    assert_list_type,
+    assert_named_type,
+    assert_non_null_type,
+    assert_nullable_type,
+    assert_object_type,
+    assert_output_type,
+    assert_scalar_type,
+    assert_type,
+    assert_union_type,
+    assert_wrapping_type,
+    get_named_type,
+    get_nullable_type,
+    is_abstract_type,
+    is_composite_type,
+    is_directive,
+    is_enum_type,
+    is_input_object_type,
+    is_input_type,
+    is_interface_type,
+    is_leaf_type,
+    is_list_type,
+    is_named_type,
+    is_required_argument,
+    is_required_input_field,
+    is_non_null_type,
+    is_nullable_type,
+    is_object_type,
+    is_output_type,
+    is_scalar_type,
+    is_specified_directive,
+    is_specified_scalar_type,
+    is_type,
+    is_union_type,
+    is_wrapping_type,
+)
+
+ObjectType = GraphQLObjectType("Object", {})
+InterfaceType = GraphQLInterfaceType("Interface", {})
+UnionType = GraphQLUnionType("Union", types=[ObjectType])
+EnumType = GraphQLEnumType("Enum", values={"foo": {}})
+InputObjectType = GraphQLInputObjectType("InputObject", {})
+ScalarType = GraphQLScalarType("Scalar")
+Directive = GraphQLDirective("Directive", [DirectiveLocation.QUERY])
+
+
+def describe_type_predicates():
+    def describe_is_type():
+        def returns_true_for_unwrapped_types():
+            assert is_type(GraphQLString) is True
+            assert_type(GraphQLString)
+            assert is_type(ObjectType) is True
+            assert_type(ObjectType)
+
+        def returns_true_for_wrapped_types():
+            assert is_type(GraphQLNonNull(GraphQLString)) is True
+            assert_type(GraphQLNonNull(GraphQLString))
+
+        def returns_false_for_type_classes_rather_than_instance():
+            assert is_type(GraphQLObjectType) is False
+            with raises(TypeError):
+                assert_type(GraphQLObjectType)
+
+        def returns_false_for_random_garbage():
+            assert is_type({"what": "is this"}) is False
+            with raises(TypeError):
+                assert_type({"what": "is this"})
+
+    def describe_is_scalar_type():
+        def returns_true_for_spec_defined_scalar():
+            assert is_scalar_type(GraphQLString) is True
+            assert_scalar_type(GraphQLString)
+
+        def returns_true_for_custom_scalar():
+            assert is_scalar_type(ScalarType) is True
+            assert_scalar_type(ScalarType)
+
+        def returns_false_for_scalar_class_rather_than_instance():
+            assert is_scalar_type(GraphQLScalarType) is False
+            with raises(TypeError):
+                assert_scalar_type(GraphQLScalarType)
+
+        def returns_false_for_wrapped_scalar():
+            assert is_scalar_type(GraphQLList(ScalarType)) is False
+            with raises(TypeError):
+                assert_scalar_type(GraphQLList(ScalarType))
+
+        def returns_false_for_non_scalar():
+            assert is_scalar_type(EnumType) is False
+            with raises(TypeError):
+                assert_scalar_type(EnumType)
+            assert is_scalar_type(Directive) is False
+            with raises(TypeError):
+                assert_scalar_type(Directive)
+
+        def returns_false_for_random_garbage():
+            assert is_scalar_type(None) is False
+            with raises(TypeError):
+                assert_scalar_type(None)
+            assert is_scalar_type({"what": "is this"}) is False
+            with raises(TypeError):
+                assert_scalar_type({"what": "is this"})
+
+    def describe_is_specified_scalar_type():
+        def returns_true_for_specified_scalars():
+            assert is_specified_scalar_type(GraphQLString) is True
+            assert is_specified_scalar_type(GraphQLInt) is True
+            assert is_specified_scalar_type(GraphQLFloat) is True
+            assert is_specified_scalar_type(GraphQLBoolean) is True
+            assert is_specified_scalar_type(GraphQLID) is True
+
+    def describe_is_object_type():
+        def returns_true_for_object_type():
+            assert is_object_type(ObjectType) is True
+            assert_object_type(ObjectType)
+
+        def returns_false_for_wrapped_object_type():
+            assert is_object_type(GraphQLList(ObjectType)) is False
+            with raises(TypeError):
+                assert_object_type(GraphQLList(ObjectType))
+
+        def returns_false_for_non_object_type():
+            assert is_scalar_type(InterfaceType) is False
+            with raises(TypeError):
+                assert_scalar_type(InterfaceType)
+
+    def describe_is_interface_type():
+        def returns_true_for_interface_type():
+            assert is_interface_type(InterfaceType) is True
+            assert_interface_type(InterfaceType)
+
+        def returns_false_for_wrapped_interface_type():
+            assert is_interface_type(GraphQLList(InterfaceType)) is False
+            with raises(TypeError):
+                assert_interface_type(GraphQLList(InterfaceType))
+
+        def returns_false_for_non_interface_type():
+            assert is_interface_type(ObjectType) is False
+            with raises(TypeError):
+                assert_interface_type(ObjectType)
+
+    def describe_is_union_type():
+        def returns_true_for_union_type():
+            assert is_union_type(UnionType) is True
+            assert_union_type(UnionType)
+
+        def returns_false_for_wrapped_union_type():
+            assert is_union_type(GraphQLList(UnionType)) is False
+            with raises(TypeError):
+                assert_union_type(GraphQLList(UnionType))
+
+        def returns_false_for_non_union_type():
+            assert is_union_type(ObjectType) is False
+            with raises(TypeError):
+                assert_union_type(ObjectType)
+
+    def describe_is_enum_type():
+        def returns_true_for_enum_type():
+            assert is_enum_type(EnumType) is True
+            assert_enum_type(EnumType)
+
+        def returns_false_for_wrapped_enum_type():
+            assert is_enum_type(GraphQLList(EnumType)) is False
+            with raises(TypeError):
+                assert_enum_type(GraphQLList(EnumType))
+
+        def returns_false_for_non_enum_type():
+            assert is_enum_type(ScalarType) is False
+            with raises(TypeError):
+                assert_enum_type(ScalarType)
+
+    def describe_is_input_object_type():
+        def returns_true_for_input_object_type():
+            assert is_input_object_type(InputObjectType) is True
+            assert_input_object_type(InputObjectType)
+
+        def returns_false_for_wrapped_input_object_type():
+            assert is_input_object_type(GraphQLList(InputObjectType)) is False
+            with raises(TypeError):
+                assert_input_object_type(GraphQLList(InputObjectType))
+
+        def returns_false_for_non_input_object_type():
+            assert is_input_object_type(ObjectType) is False
+            with raises(TypeError):
+                assert_input_object_type(ObjectType)
+
+    def describe_is_list_type():
+        def returns_true_for_a_list_wrapped_type():
+            assert is_list_type(GraphQLList(ObjectType)) is True
+            assert_list_type(GraphQLList(ObjectType))
+
+        def returns_false_for_a_unwrapped_type():
+            assert is_list_type(ObjectType) is False
+            with raises(TypeError):
+                assert_list_type(ObjectType)
+
+        def returns_false_for_a_non_list_wrapped_type():
+            assert is_list_type(GraphQLNonNull(GraphQLList(ObjectType))) is False
+            with raises(TypeError):
+                assert_list_type(GraphQLNonNull(GraphQLList(ObjectType)))
+
+    def describe_is_non_null_type():
+        def returns_true_for_a_non_null_wrapped_type():
+            assert is_non_null_type(GraphQLNonNull(ObjectType)) is True
+            assert_non_null_type(GraphQLNonNull(ObjectType))
+
+        def returns_false_for_an_unwrapped_type():
+            assert is_non_null_type(ObjectType) is False
+            with raises(TypeError):
+                assert_non_null_type(ObjectType)
+
+        def returns_false_for_a_not_non_null_wrapped_type():
+            assert is_non_null_type(GraphQLList(GraphQLNonNull(ObjectType))) is False
+            with raises(TypeError):
+                assert_non_null_type(GraphQLList(GraphQLNonNull(ObjectType)))
+
+    def describe_is_input_type():
+        def _assert_input_type(type_):
+            assert is_input_type(type_) is True
+            assert_input_type(type_)
+
+        def returns_true_for_an_input_type():
+            _assert_input_type(GraphQLString)
+            _assert_input_type(EnumType)
+            _assert_input_type(InputObjectType)
+
+        def returns_true_for_a_wrapped_input_type():
+            _assert_input_type(GraphQLList(GraphQLString))
+            _assert_input_type(GraphQLList(EnumType))
+            _assert_input_type(GraphQLList(InputObjectType))
+
+            _assert_input_type(GraphQLNonNull(GraphQLString))
+            _assert_input_type(GraphQLNonNull(EnumType))
+            _assert_input_type(GraphQLNonNull(InputObjectType))
+
+        def _assert_non_input_type(type_):
+            assert is_input_type(type_) is False
+            with raises(TypeError):
+                assert_input_type(type_)
+
+        def returns_false_for_an_output_type():
+            _assert_non_input_type(ObjectType)
+            _assert_non_input_type(InterfaceType)
+            _assert_non_input_type(UnionType)
+
+        def returns_false_for_a_wrapped_output_type():
+            _assert_non_input_type(GraphQLList(ObjectType))
+            _assert_non_input_type(GraphQLList(InterfaceType))
+            _assert_non_input_type(GraphQLList(UnionType))
+
+            _assert_non_input_type(GraphQLNonNull(ObjectType))
+            _assert_non_input_type(GraphQLNonNull(InterfaceType))
+            _assert_non_input_type(GraphQLNonNull(UnionType))
+
+    def describe_is_output_type():
+        def _assert_output_type(type_):
+            assert is_output_type(type_) is True
+            assert_output_type(type_)
+
+        def returns_true_for_an_output_type():
+            _assert_output_type(GraphQLString)
+            _assert_output_type(ObjectType)
+            _assert_output_type(InterfaceType)
+            _assert_output_type(UnionType)
+            _assert_output_type(EnumType)
+
+        def returns_true_for_a_wrapped_output_type():
+            _assert_output_type(GraphQLList(GraphQLString))
+            _assert_output_type(GraphQLList(ObjectType))
+            _assert_output_type(GraphQLList(InterfaceType))
+            _assert_output_type(GraphQLList(UnionType))
+            _assert_output_type(GraphQLList(EnumType))
+
+            _assert_output_type(GraphQLNonNull(GraphQLString))
+            _assert_output_type(GraphQLNonNull(ObjectType))
+            _assert_output_type(GraphQLNonNull(InterfaceType))
+            _assert_output_type(GraphQLNonNull(UnionType))
+            _assert_output_type(GraphQLNonNull(EnumType))
+
+        def _assert_non_output_type(type_):
+            assert is_output_type(type_) is False
+            with raises(TypeError):
+                assert_output_type(type_)
+
+        def returns_false_for_an_input_type():
+            _assert_non_output_type(InputObjectType)
+
+        def returns_false_for_a_wrapped_input_type():
+            _assert_non_output_type(GraphQLList(InputObjectType))
+            _assert_non_output_type(GraphQLNonNull(InputObjectType))
+
+    def describe_is_leaf_type():
+        def returns_true_for_scalar_and_enum_types():
+            assert is_leaf_type(ScalarType) is True
+            assert_leaf_type(ScalarType)
+            assert is_leaf_type(EnumType) is True
+            assert_leaf_type(EnumType)
+
+        def returns_false_for_wrapped_leaf_type():
+            assert is_leaf_type(GraphQLList(ScalarType)) is False
+            with raises(TypeError):
+                assert_leaf_type(GraphQLList(ScalarType))
+
+        def returns_false_for_non_leaf_type():
+            assert is_leaf_type(ObjectType) is False
+            with raises(TypeError):
+                assert_leaf_type(ObjectType)
+
+        def returns_false_for_wrapped_non_leaf_type():
+            assert is_leaf_type(GraphQLList(ObjectType)) is False
+            with raises(TypeError):
+                assert_leaf_type(GraphQLList(ObjectType))
+
+    def describe_is_composite_type():
+        def returns_true_for_object_interface_and_union_types():
+            assert is_composite_type(ObjectType) is True
+            assert_composite_type(ObjectType)
+            assert is_composite_type(InterfaceType) is True
+            assert_composite_type(InterfaceType)
+            assert is_composite_type(UnionType) is True
+            assert_composite_type(UnionType)
+
+        def returns_false_for_wrapped_composite_type():
+            assert is_composite_type(GraphQLList(ObjectType)) is False
+            with raises(TypeError):
+                assert_composite_type(GraphQLList(ObjectType))
+
+        def returns_false_for_non_composite_type():
+            assert is_composite_type(InputObjectType) is False
+            with raises(TypeError):
+                assert_composite_type(InputObjectType)
+
+        def returns_false_for_wrapped_non_composite_type():
+            assert is_composite_type(GraphQLList(InputObjectType)) is False
+            with raises(TypeError):
+                assert_composite_type(GraphQLList(InputObjectType))
+
+    def describe_is_abstract_type():
+        def returns_true_for_interface_and_union_types():
+            assert is_abstract_type(InterfaceType) is True
+            assert_abstract_type(InterfaceType)
+            assert is_abstract_type(UnionType) is True
+            assert_abstract_type(UnionType)
+
+        def returns_false_for_wrapped_abstract_type():
+            assert is_abstract_type(GraphQLList(InterfaceType)) is False
+            with raises(TypeError):
+                assert_abstract_type(GraphQLList(InterfaceType))
+
+        def returns_false_for_non_abstract_type():
+            assert is_abstract_type(ObjectType) is False
+            with raises(TypeError):
+                assert_abstract_type(ObjectType)
+
+        def returns_false_for_wrapped_non_abstract_type():
+            assert is_abstract_type(GraphQLList(ObjectType)) is False
+            with raises(TypeError):
+                assert_abstract_type(GraphQLList(ObjectType))
+
+    def describe_is_wrapping_type():
+        def returns_true_for_list_and_non_null_types():
+            assert is_wrapping_type(GraphQLList(ObjectType)) is True
+            assert_wrapping_type(GraphQLList(ObjectType))
+            assert is_wrapping_type(GraphQLNonNull(ObjectType)) is True
+            assert_wrapping_type(GraphQLNonNull(ObjectType))
+
+        def returns_false_for_unwrapped_types():
+            assert is_wrapping_type(ObjectType) is False
+            with raises(TypeError):
+                assert_wrapping_type(ObjectType)
+
+    def describe_is_nullable_type():
+        def returns_true_for_unwrapped_types():
+            assert is_nullable_type(ObjectType) is True
+            assert_nullable_type(ObjectType)
+
+        def returns_true_for_list_of_non_null_types():
+            assert is_nullable_type(GraphQLList(GraphQLNonNull(ObjectType))) is True
+            assert_nullable_type(GraphQLList(GraphQLNonNull(ObjectType)))
+
+        def returns_false_for_non_null_types():
+            assert is_nullable_type(GraphQLNonNull(ObjectType)) is False
+            with raises(TypeError):
+                assert_nullable_type(GraphQLNonNull(ObjectType))
+
+    def describe_get_nullable_type():
+        def returns_none_for_no_type():
+            assert get_nullable_type(None) is None
+
+        def returns_self_for_a_nullable_type():
+            assert get_nullable_type(ObjectType) is ObjectType
+            list_of_obj = GraphQLList(ObjectType)
+            assert get_nullable_type(list_of_obj) is list_of_obj
+
+        def unwraps_non_null_type():
+            assert get_nullable_type(GraphQLNonNull(ObjectType)) is ObjectType
+
+    def describe_is_named_type():
+        def returns_true_for_unwrapped_types():
+            assert is_named_type(ObjectType) is True
+            assert_named_type(ObjectType)
+
+        def returns_false_for_list_and_non_null_types():
+            assert is_named_type(GraphQLList(ObjectType)) is False
+            with raises(TypeError):
+                assert_named_type(GraphQLList(ObjectType))
+            assert is_named_type(GraphQLNonNull(ObjectType)) is False
+            with raises(TypeError):
+                assert_named_type(GraphQLNonNull(ObjectType))
+
+    def describe_get_named_type():
+        def returns_none_for_no_type():
+            assert get_named_type(None) is None
+
+        def returns_self_for_an_unwrapped_type():
+            assert get_named_type(ObjectType) is ObjectType
+
+        def unwraps_wrapper_types():
+            assert get_named_type(GraphQLNonNull(ObjectType)) is ObjectType
+            assert get_named_type(GraphQLList(ObjectType)) is ObjectType
+
+        def unwraps_deeply_wrapper_types():
+            assert (
+                get_named_type(GraphQLNonNull(GraphQLList(GraphQLNonNull(ObjectType))))
+                is ObjectType
+            )
+
+    def describe_is_required_argument():
+        def returns_true_for_required_arguments():
+            required_arg = GraphQLArgument(GraphQLNonNull(GraphQLString))
+            assert is_required_argument(required_arg) is True
+
+        def returns_false_for_optional_arguments():
+            opt_arg1 = GraphQLArgument(GraphQLString)
+            assert is_required_argument(opt_arg1) is False
+
+            opt_arg2 = GraphQLArgument(GraphQLString, default_value=None)
+            assert is_required_argument(opt_arg2) is False
+
+            opt_arg3 = GraphQLArgument(GraphQLList(GraphQLNonNull(GraphQLString)))
+            assert is_required_argument(opt_arg3) is False
+
+            opt_arg4 = GraphQLArgument(
+                GraphQLNonNull(GraphQLString), default_value="default"
+            )
+            assert is_required_argument(opt_arg4) is False
+
+    def describe_is_required_input_field():
+        def returns_true_for_required_input_field():
+            required_field = GraphQLInputField(GraphQLNonNull(GraphQLString))
+            assert is_required_input_field(required_field) is True
+
+        def returns_false_for_optional_input_field():
+            opt_field1 = GraphQLInputField(GraphQLString)
+            assert is_required_input_field(opt_field1) is False
+
+            opt_field2 = GraphQLInputField(GraphQLString, default_value=None)
+            assert is_required_input_field(opt_field2) is False
+
+            opt_field3 = GraphQLInputField(GraphQLList(GraphQLNonNull(GraphQLString)))
+            assert is_required_input_field(opt_field3) is False
+
+            opt_field4 = GraphQLInputField(
+                GraphQLNonNull(GraphQLString), default_value="default"
+            )
+            assert is_required_input_field(opt_field4) is False
+
+    def describe_directive_predicates():
+        def describe_is_directive():
+            def returns_true_for_spec_defined_directive():
+                assert is_directive(GraphQLSkipDirective) is True
+                assert_directive(GraphQLSkipDirective)
+
+            def returns_true_for_custom_directive():
+                assert is_directive(Directive) is True
+                assert_directive(Directive)
+
+            def returns_false_for_directive_class_rather_than_instance():
+                assert is_directive(GraphQLDirective) is False
+                with raises(TypeError):
+                    assert_directive(GraphQLScalarType)
+
+            def returns_false_for_non_directive():
+                assert is_directive(EnumType) is False
+                with raises(TypeError):
+                    assert_directive(EnumType)
+                assert is_directive(ScalarType) is False
+                with raises(TypeError):
+                    assert_directive(ScalarType)
+
+            def returns_false_for_random_garbage():
+                assert is_directive(None) is False
+                with raises(TypeError):
+                    assert_directive(None)
+                assert is_directive({"what": "is this"}) is False
+                with raises(TypeError):
+                    assert_directive({"what": "is this"})
+
+        def describe_is_specified_directive():
+            def returns_true_for_specified_directives():
+                assert is_specified_directive(GraphQLIncludeDirective) is True
+                assert is_specified_directive(GraphQLSkipDirective) is True
+                assert is_specified_directive(GraphQLDeprecatedDirective) is True
+
+            def returns_false_for_custom_directive():
+                assert is_specified_directive(Directive) is False
diff --git a/tests/type/test_scalars.py b/tests/type/test_scalars.py
new file mode 100644
index 0000000..61bac11
--- /dev/null
+++ b/tests/type/test_scalars.py
@@ -0,0 +1,441 @@
+from math import inf, nan, pi
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.language import parse_value as parse_value_to_ast
+from graphql.pyutils import Undefined
+from graphql.type import (
+    GraphQLInt,
+    GraphQLFloat,
+    GraphQLString,
+    GraphQLBoolean,
+    GraphQLID,
+)
+
+
+def describe_type_system_specified_scalar_types():
+    def describe_graphql_int():
+        def parse_value():
+            _parse_value = GraphQLInt.parse_value
+
+            def _parse_value_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_value(s)
+                assert str(exc_info.value) == message
+
+            assert _parse_value(1) == 1
+            assert _parse_value(0) == 0
+            assert _parse_value(-1) == -1
+
+            _parse_value_raises(
+                9876504321,
+                "Int cannot represent non 32-bit signed integer value: 9876504321",
+            )
+            _parse_value_raises(
+                -9876504321,
+                "Int cannot represent non 32-bit signed integer value: -9876504321",
+            )
+            _parse_value_raises(0.1, "Int cannot represent non-integer value: 0.1")
+            _parse_value_raises(nan, "Int cannot represent non-integer value: nan")
+            _parse_value_raises(inf, "Int cannot represent non-integer value: inf")
+            _parse_value_raises(
+                Undefined, "Int cannot represent non-integer value: Undefined"
+            )
+            _parse_value_raises(None, "Int cannot represent non-integer value: None")
+            _parse_value_raises("", "Int cannot represent non-integer value: ''")
+            _parse_value_raises("123", "Int cannot represent non-integer value: '123'")
+            _parse_value_raises(False, "Int cannot represent non-integer value: False")
+            _parse_value_raises(True, "Int cannot represent non-integer value: True")
+            _parse_value_raises([1], "Int cannot represent non-integer value: [1]")
+            _parse_value_raises(
+                {"value": 1}, "Int cannot represent non-integer value: {'value': 1}"
+            )
+
+        def parse_literal():
+            def _parse_literal(s):
+                return GraphQLInt.parse_literal(parse_value_to_ast(s))
+
+            def _parse_literal_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_literal(s)
+                assert str(exc_info.value).startswith(message + "\n")
+
+            assert _parse_literal("1") == 1
+            assert _parse_literal("0") == 0
+            assert _parse_literal("-1") == -1
+
+            _parse_literal_raises(
+                "9876504321",
+                "Int cannot represent non 32-bit signed integer value: 9876504321",
+            )
+            _parse_literal_raises(
+                "-9876504321",
+                "Int cannot represent non 32-bit signed integer value: -9876504321",
+            )
+            _parse_literal_raises("1.0", "Int cannot represent non-integer value: 1.0")
+            _parse_literal_raises(
+                "null", "Int cannot represent non-integer value: null"
+            )
+            _parse_literal_raises(
+                "None", "Int cannot represent non-integer value: None"
+            )
+            _parse_literal_raises('""', 'Int cannot represent non-integer value: ""')
+            _parse_literal_raises(
+                '"123"', 'Int cannot represent non-integer value: "123"'
+            )
+            _parse_literal_raises(
+                "false", "Int cannot represent non-integer value: false"
+            )
+            _parse_literal_raises(
+                "False", "Int cannot represent non-integer value: False"
+            )
+            _parse_literal_raises("[1]", "Int cannot represent non-integer value: [1]")
+            _parse_literal_raises(
+                "{value: 1}", "Int cannot represent non-integer value: {value: 1}"
+            )
+            _parse_literal_raises(
+                "ENUM_VALUE", "Int cannot represent non-integer value: ENUM_VALUE"
+            )
+            _parse_literal_raises(
+                "$var", "Int cannot represent non-integer value: $var"
+            )
+
+    def describe_graphql_float():
+        def parse_value():
+            _parse_value = GraphQLFloat.parse_value
+
+            def _parse_value_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_value(s)
+                assert str(exc_info.value) == message
+
+            assert _parse_value(1) == 1
+            assert _parse_value(0) == 0
+            assert _parse_value(-1) == -1
+            assert _parse_value(0.1) == 0.1
+            assert _parse_value(pi) == pi
+
+            _parse_value_raises(nan, "Float cannot represent non numeric value: nan")
+            _parse_value_raises(inf, "Float cannot represent non numeric value: inf")
+            _parse_value_raises("", "Float cannot represent non numeric value: ''")
+            _parse_value_raises(
+                "123", "Float cannot represent non numeric value: '123'"
+            )
+            _parse_value_raises(
+                "123.5", "Float cannot represent non numeric value: '123.5'"
+            )
+            _parse_value_raises(
+                False, "Float cannot represent non numeric value: False"
+            )
+            _parse_value_raises(True, "Float cannot represent non numeric value: True")
+            _parse_value_raises(
+                [0.1], "Float cannot represent non numeric value: [0.1]"
+            )
+            _parse_value_raises(
+                {"value": 0.1},
+                "Float cannot represent non numeric value: {'value': 0.1}",
+            )
+
+        def parse_literal():
+            def _parse_literal(s):
+                return GraphQLFloat.parse_literal(parse_value_to_ast(s))
+
+            def _parse_literal_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_literal(s)
+                assert str(exc_info.value).startswith(message + "\n")
+
+            assert _parse_literal("1") == 1
+            assert _parse_literal("0") == 0
+            assert _parse_literal("-1") == -1
+            assert _parse_literal("0.1") == 0.1
+            assert _parse_literal(str(pi)) == pi
+
+            _parse_literal_raises(
+                "null", "Float cannot represent non numeric value: null"
+            )
+            _parse_literal_raises(
+                "None", "Float cannot represent non numeric value: None"
+            )
+            _parse_literal_raises('""', 'Float cannot represent non numeric value: ""')
+            _parse_literal_raises(
+                '"123"', 'Float cannot represent non numeric value: "123"'
+            )
+            _parse_literal_raises(
+                '"123.5"', 'Float cannot represent non numeric value: "123.5"'
+            )
+            _parse_literal_raises(
+                "false", "Float cannot represent non numeric value: false"
+            )
+            _parse_literal_raises(
+                "False", "Float cannot represent non numeric value: False"
+            )
+            _parse_literal_raises(
+                "[0.1]", "Float cannot represent non numeric value: [0.1]"
+            )
+            _parse_literal_raises(
+                "{value: 0.1}", "Float cannot represent non numeric value: {value: 0.1}"
+            )
+            _parse_literal_raises(
+                "ENUM_VALUE", "Float cannot represent non numeric value: ENUM_VALUE"
+            )
+            _parse_literal_raises(
+                "$var", "Float cannot represent non numeric value: $var"
+            )
+
+    def describe_graphql_string():
+        def parse_value():
+            _parse_value = GraphQLString.parse_value
+
+            def _parse_value_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_value(s)
+                assert str(exc_info.value) == message
+
+            assert _parse_value("foo") == "foo"
+
+            _parse_value_raises(
+                Undefined, "String cannot represent a non string value: Undefined"
+            )
+            _parse_value_raises(
+                None, "String cannot represent a non string value: None"
+            )
+            _parse_value_raises(1, "String cannot represent a non string value: 1")
+            _parse_value_raises(nan, "String cannot represent a non string value: nan")
+            _parse_value_raises(
+                False, "String cannot represent a non string value: False"
+            )
+            _parse_value_raises(
+                ["foo"], "String cannot represent a non string value: ['foo']"
+            )
+            _parse_value_raises(
+                {"value": "foo"},
+                "String cannot represent a non string value: {'value': 'foo'}",
+            )
+
+        def parse_literal():
+            def _parse_literal(s):
+                return GraphQLString.parse_literal(parse_value_to_ast(s))
+
+            def _parse_literal_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_literal(s)
+                assert str(exc_info.value).startswith(message + "\n")
+
+            assert _parse_literal('"foo"') == "foo"
+            assert _parse_literal('"""bar"""') == "bar"
+
+            _parse_literal_raises(
+                "null", "String cannot represent a non string value: null"
+            )
+            _parse_literal_raises(
+                "None", "String cannot represent a non string value: None"
+            )
+            _parse_literal_raises("1", "String cannot represent a non string value: 1")
+            _parse_literal_raises(
+                "0.1", "String cannot represent a non string value: 0.1"
+            )
+            _parse_literal_raises(
+                "false", "String cannot represent a non string value: false"
+            )
+            _parse_literal_raises(
+                "False", "String cannot represent a non string value: False"
+            )
+            _parse_literal_raises(
+                '["foo"]', 'String cannot represent a non string value: ["foo"]'
+            )
+            _parse_literal_raises(
+                '{value: "foo"}',
+                'String cannot represent a non string value: {value: "foo"}',
+            )
+            _parse_literal_raises(
+                "ENUM_VALUE", "String cannot represent a non string value: ENUM_VALUE"
+            )
+            _parse_literal_raises(
+                "$var", "String cannot represent a non string value: $var"
+            )
+
+    def describe_graphql_boolean():
+        def parse_value():
+            _parse_value = GraphQLBoolean.parse_value
+
+            def _parse_value_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_value(s)
+                assert str(exc_info.value) == message
+
+            assert _parse_value(True) is True
+            assert _parse_value(False) is False
+
+            _parse_value_raises(
+                Undefined, "Boolean cannot represent a non boolean value: Undefined"
+            )
+            _parse_value_raises(
+                None, "Boolean cannot represent a non boolean value: None"
+            )
+            _parse_value_raises(0, "Boolean cannot represent a non boolean value: 0")
+            _parse_value_raises(1, "Boolean cannot represent a non boolean value: 1")
+            _parse_value_raises(
+                nan, "Boolean cannot represent a non boolean value: nan"
+            )
+            _parse_value_raises("", "Boolean cannot represent a non boolean value: ''")
+            _parse_value_raises(
+                "false", "Boolean cannot represent a non boolean value: 'false'"
+            )
+            _parse_value_raises(
+                "False", "Boolean cannot represent a non boolean value: 'False'"
+            )
+            _parse_value_raises(
+                [False], "Boolean cannot represent a non boolean value: [False]"
+            )
+            _parse_value_raises(
+                {"value": False},
+                "Boolean cannot represent a non boolean value: {'value': False}",
+            )
+
+        def parse_literal():
+            def _parse_literal(s):
+                return GraphQLBoolean.parse_literal(parse_value_to_ast(s))
+
+            def _parse_literal_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_literal(s)
+                assert str(exc_info.value).startswith(message + "\n")
+
+            assert _parse_literal("true") is True
+            assert _parse_literal("false") is False
+
+            _parse_literal_raises(
+                "True", "Boolean cannot represent a non boolean value: True"
+            )
+            _parse_literal_raises(
+                "False", "Boolean cannot represent a non boolean value: False"
+            )
+            _parse_literal_raises(
+                "null", "Boolean cannot represent a non boolean value: null"
+            )
+            _parse_literal_raises(
+                "None", "Boolean cannot represent a non boolean value: None"
+            )
+            _parse_literal_raises(
+                "0", "Boolean cannot represent a non boolean value: 0"
+            )
+            _parse_literal_raises(
+                "1", "Boolean cannot represent a non boolean value: 1"
+            )
+            _parse_literal_raises(
+                "0.1", "Boolean cannot represent a non boolean value: 0.1"
+            )
+            _parse_literal_raises(
+                '""', 'Boolean cannot represent a non boolean value: ""'
+            )
+            _parse_literal_raises(
+                '"false"', 'Boolean cannot represent a non boolean value: "false"'
+            )
+            _parse_literal_raises(
+                '"False"', 'Boolean cannot represent a non boolean value: "False"'
+            )
+            _parse_literal_raises(
+                "[false]", "Boolean cannot represent a non boolean value: [false]"
+            )
+            _parse_literal_raises(
+                "[False]", "Boolean cannot represent a non boolean value: [False]"
+            )
+            _parse_literal_raises(
+                "{value: false}",
+                "Boolean cannot represent a non boolean value: {value: false}",
+            )
+            _parse_literal_raises(
+                "{value: False}",
+                "Boolean cannot represent a non boolean value: {value: False}",
+            )
+            _parse_literal_raises(
+                "ENUM_VALUE", "Boolean cannot represent a non boolean value: ENUM_VALUE"
+            )
+            _parse_literal_raises(
+                "$var", "Boolean cannot represent a non boolean value: $var"
+            )
+
+    def describe_graphql_id():
+        def parse_value():
+            _parse_value = GraphQLID.parse_value
+
+            def _parse_value_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_value(s)
+                assert str(exc_info.value) == message
+
+            assert _parse_value("") == ""
+            assert _parse_value("1") == "1"
+            assert _parse_value("foo") == "foo"
+            assert _parse_value(1) == "1"
+            assert _parse_value(0) == "0"
+            assert _parse_value(-1) == "-1"
+
+            # Maximum and minimum safe numbers in JS
+            assert _parse_value(9007199254740991) == "9007199254740991"
+            assert _parse_value(-9007199254740991) == "-9007199254740991"
+
+            _parse_value_raises(Undefined, "ID cannot represent value: Undefined")
+            _parse_value_raises(None, "ID cannot represent value: None")
+            _parse_value_raises(0.1, "ID cannot represent value: 0.1")
+            _parse_value_raises(nan, "ID cannot represent value: nan")
+            _parse_value_raises(inf, "ID cannot represent value: inf")
+            _parse_value_raises(False, "ID cannot represent value: False")
+            _parse_value_raises(["1"], "ID cannot represent value: ['1']")
+            _parse_value_raises(
+                {"value": "1"}, "ID cannot represent value: {'value': '1'}"
+            )
+
+        def parse_literal():
+            def _parse_literal(s):
+                return GraphQLID.parse_literal(parse_value_to_ast(s))
+
+            def _parse_literal_raises(s, message):
+                with raises(GraphQLError) as exc_info:
+                    _parse_literal(s)
+                assert str(exc_info.value).startswith(message + "\n")
+
+            assert _parse_literal('""') == ""
+            assert _parse_literal('"1"') == "1"
+            assert _parse_literal('"foo"') == "foo"
+            assert _parse_literal('"""foo"""') == "foo"
+            assert _parse_literal("1") == "1"
+            assert _parse_literal("0") == "0"
+            assert _parse_literal("-1") == "-1"
+
+            # Support arbitrary long numbers even if they can't be represented in JS
+            assert _parse_literal("90071992547409910") == "90071992547409910"
+            assert _parse_literal("-90071992547409910") == "-90071992547409910"
+
+            _parse_literal_raises(
+                "null", "ID cannot represent a non-string and non-integer value: null"
+            )
+            _parse_literal_raises(
+                "None", "ID cannot represent a non-string and non-integer value: None"
+            )
+            _parse_literal_raises(
+                "0.1", "ID cannot represent a non-string and non-integer value: 0.1"
+            )
+            _parse_literal_raises(
+                "false", "ID cannot represent a non-string and non-integer value: false"
+            )
+            _parse_literal_raises(
+                "False", "ID cannot represent a non-string and non-integer value: False"
+            )
+            _parse_literal_raises(
+                '["1"]', 'ID cannot represent a non-string and non-integer value: ["1"]'
+            )
+            _parse_literal_raises(
+                '{value: "1"}',
+                "ID cannot represent a non-string and non-integer value:"
+                ' {value: "1"}',
+            )
+            _parse_literal_raises(
+                "ENUM_VALUE",
+                "ID cannot represent a non-string and non-integer value: ENUM_VALUE",
+            )
+            _parse_literal_raises(
+                "$var", "ID cannot represent a non-string and non-integer value: $var"
+            )
diff --git a/tests/type/test_schema.py b/tests/type/test_schema.py
new file mode 100644
index 0000000..5c6245e
--- /dev/null
+++ b/tests/type/test_schema.py
@@ -0,0 +1,436 @@
+from pytest import raises  # type: ignore
+
+from graphql.language import (
+    DirectiveLocation,
+    SchemaDefinitionNode,
+    SchemaExtensionNode,
+    TypeDefinitionNode,
+    TypeExtensionNode,
+)
+from graphql.pyutils import FrozenList
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLDirective,
+    GraphQLField,
+    GraphQLFieldMap,
+    GraphQLInputObjectType,
+    GraphQLInputField,
+    GraphQLInt,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLObjectType,
+    GraphQLScalarType,
+    GraphQLSchema,
+    GraphQLString,
+    GraphQLType,
+    specified_directives,
+)
+from graphql.utilities import print_schema
+
+from ..utils import dedent
+
+
+def describe_type_system_schema():
+    def define_sample_schema():
+        BlogImage = GraphQLObjectType(
+            "Image",
+            {
+                "url": GraphQLField(GraphQLString),
+                "width": GraphQLField(GraphQLInt),
+                "height": GraphQLField(GraphQLInt),
+            },
+        )
+
+        BlogArticle: GraphQLObjectType
+
+        BlogAuthor = GraphQLObjectType(
+            "Author",
+            lambda: {
+                "id": GraphQLField(GraphQLString),
+                "name": GraphQLField(GraphQLString),
+                "pic": GraphQLField(
+                    BlogImage,
+                    args={
+                        "width": GraphQLArgument(GraphQLInt),
+                        "height": GraphQLArgument(GraphQLInt),
+                    },
+                ),
+                "recentArticle": GraphQLField(BlogArticle),
+            },
+        )
+
+        BlogArticle = GraphQLObjectType(
+            "Article",
+            lambda: {
+                "id": GraphQLField(GraphQLString),
+                "isPublished": GraphQLField(GraphQLBoolean),
+                "author": GraphQLField(BlogAuthor),
+                "title": GraphQLField(GraphQLString),
+                "body": GraphQLField(GraphQLString),
+            },
+        )
+
+        BlogQuery = GraphQLObjectType(
+            "Query",
+            {
+                "article": GraphQLField(
+                    BlogArticle, args={"id": GraphQLArgument(GraphQLString)}
+                ),
+                "feed": GraphQLField(GraphQLList(BlogArticle)),
+            },
+        )
+
+        BlogMutation = GraphQLObjectType(
+            "Mutation", {"writeArticle": GraphQLField(BlogArticle)}
+        )
+
+        BlogSubscription = GraphQLObjectType(
+            "Subscription",
+            {
+                "articleSubscribe": GraphQLField(
+                    args={"id": GraphQLArgument(GraphQLString)}, type_=BlogArticle
+                )
+            },
+        )
+
+        schema = GraphQLSchema(
+            BlogQuery, BlogMutation, BlogSubscription, description="Sample schema",
+        )
+
+        kwargs = schema.to_kwargs()
+        types = kwargs.pop("types")
+        assert types == list(schema.type_map.values())
+        assert kwargs == {
+            "query": BlogQuery,
+            "mutation": BlogMutation,
+            "subscription": BlogSubscription,
+            "directives": specified_directives,
+            "description": "Sample schema",
+            "extensions": None,
+            "ast_node": None,
+            "extension_ast_nodes": [],
+            "assume_valid": False,
+        }
+
+        assert print_schema(schema) == dedent(
+            '''
+            """Sample schema"""
+            schema {
+              query: Query
+              mutation: Mutation
+              subscription: Subscription
+            }
+
+            type Query {
+              article(id: String): Article
+              feed: [Article]
+            }
+
+            type Article {
+              id: String
+              isPublished: Boolean
+              author: Author
+              title: String
+              body: String
+            }
+
+            type Author {
+              id: String
+              name: String
+              pic(width: Int, height: Int): Image
+              recentArticle: Article
+            }
+
+            type Image {
+              url: String
+              width: Int
+              height: Int
+            }
+
+            type Mutation {
+              writeArticle: Article
+            }
+
+            type Subscription {
+              articleSubscribe(id: String): Article
+            }
+            '''
+        )
+
+    def freezes_the_specified_directives():
+        directives = [GraphQLDirective("SomeDirective", [])]
+        schema = GraphQLSchema(directives=directives)
+        assert isinstance(schema.directives, FrozenList)
+        assert schema.directives == directives
+        directives = schema.directives
+        schema = GraphQLSchema(directives=directives)
+        assert schema.directives is directives
+
+    def rejects_a_schema_with_incorrectly_typed_description():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLSchema(description=[])  # type: ignore
+        assert str(exc_info.value) == "Schema description must be a string."
+
+    def describe_type_map():
+        def includes_interface_possible_types_in_the_type_map():
+            SomeInterface = GraphQLInterfaceType("SomeInterface", {})
+            SomeSubtype = GraphQLObjectType(
+                "SomeSubtype", {}, interfaces=[SomeInterface]
+            )
+            schema = GraphQLSchema(
+                query=GraphQLObjectType(
+                    "Query", {"iface": GraphQLField(SomeInterface)}
+                ),
+                types=[SomeSubtype],
+            )
+            assert schema.type_map["SomeInterface"] is SomeInterface
+            assert schema.type_map["SomeSubtype"] is SomeSubtype
+
+            assert schema.is_sub_type(SomeInterface, SomeSubtype) is True
+            assert schema.is_possible_type(SomeInterface, SomeSubtype) is True
+
+        def includes_interfaces_thunk_subtypes_in_the_type_map():
+            AnotherInterface = GraphQLInterfaceType("AnotherInterface", {})
+            SomeInterface = GraphQLInterfaceType(
+                "SomeInterface", {}, interfaces=lambda: [AnotherInterface]
+            )
+            SomeSubtype = GraphQLObjectType(
+                "SomeSubtype", {}, interfaces=lambda: [SomeInterface]
+            )
+            schema = GraphQLSchema(types=[SomeSubtype],)
+            assert schema.type_map["SomeInterface"] is SomeInterface
+            assert schema.type_map["AnotherInterface"] is AnotherInterface
+            assert schema.type_map["SomeSubtype"] is SomeSubtype
+
+        def includes_nested_input_objects_in_the_map():
+            NestedInputObject = GraphQLInputObjectType("NestedInputObject", {})
+            SomeInputObject = GraphQLInputObjectType(
+                "SomeInputObject", {"nested": GraphQLInputField(NestedInputObject)}
+            )
+
+            schema = GraphQLSchema(
+                GraphQLObjectType(
+                    "Query",
+                    {
+                        "something": GraphQLField(
+                            GraphQLString, {"input": GraphQLArgument(SomeInputObject)}
+                        )
+                    },
+                )
+            )
+            assert schema.type_map["SomeInputObject"] is SomeInputObject
+            assert schema.type_map["NestedInputObject"] is NestedInputObject
+
+        def includes_input_types_only_used_in_directives():
+            directive = GraphQLDirective(
+                name="dir",
+                locations=[DirectiveLocation.OBJECT],
+                args={
+                    "arg": GraphQLArgument(
+                        GraphQLInputObjectType(
+                            "Foo", {"field": GraphQLInputField(GraphQLString)}
+                        )
+                    ),
+                    "argList": GraphQLArgument(
+                        GraphQLList(
+                            GraphQLInputObjectType(
+                                "Bar", {"field": GraphQLInputField(GraphQLString)}
+                            )
+                        )
+                    ),
+                },
+            )
+            schema = GraphQLSchema(directives=[directive])
+            assert "Foo" in schema.type_map
+            assert "Bar" in schema.type_map
+
+    def preserves_the_order_of_user_provided_types():
+        a_type = GraphQLObjectType(
+            "A", {"sub": GraphQLField(GraphQLScalarType("ASub"))}
+        )
+        z_type = GraphQLObjectType(
+            "Z", {"sub": GraphQLField(GraphQLScalarType("ZSub"))}
+        )
+        query_type = GraphQLObjectType(
+            "Query",
+            {
+                "a": GraphQLField(a_type),
+                "z": GraphQLField(z_type),
+                "sub": GraphQLField(GraphQLScalarType("QuerySub")),
+            },
+        )
+        schema = GraphQLSchema(query_type, types=[z_type, query_type, a_type])
+
+        type_names = list(schema.type_map)
+        assert type_names == [
+            "Z",
+            "ZSub",
+            "Query",
+            "QuerySub",
+            "A",
+            "ASub",
+            "Boolean",
+            "String",
+            "__Schema",
+            "__Type",
+            "__TypeKind",
+            "__Field",
+            "__InputValue",
+            "__EnumValue",
+            "__Directive",
+            "__DirectiveLocation",
+        ]
+
+        # Also check that this order is stable
+        copy_schema = GraphQLSchema(**schema.to_kwargs())
+        assert list(copy_schema.type_map) == type_names
+
+    def describe_validity():
+        def describe_when_not_assumed_valid():
+            def configures_the_schema_to_still_needing_validation():
+                # noinspection PyProtectedMember
+                assert GraphQLSchema(assume_valid=False).validation_errors is None
+
+            def checks_the_configuration_for_mistakes():
+                def query():
+                    pass
+
+                with raises(Exception):
+                    # noinspection PyTypeChecker
+                    GraphQLSchema(query)  # type: ignore
+                with raises(Exception):
+                    GraphQLSchema(types={})
+                with raises(Exception):
+                    GraphQLSchema(directives={})
+
+            def check_that_query_mutation_and_subscription_are_graphql_types():
+                directive = GraphQLDirective("foo", [])
+                with raises(TypeError) as exc_info:
+                    # noinspection PyTypeChecker
+                    GraphQLSchema(query=directive)  # type: ignore
+                assert str(exc_info.value) == "Expected query to be a GraphQL type."
+                with raises(TypeError) as exc_info:
+                    # noinspection PyTypeChecker
+                    GraphQLSchema(mutation=directive)  # type: ignore
+                assert str(exc_info.value) == (
+                    "Expected mutation to be a GraphQL type."
+                )
+                with raises(TypeError) as exc_info:
+                    # noinspection PyTypeChecker
+                    GraphQLSchema(subscription=directive)  # type: ignore
+                assert str(exc_info.value) == (
+                    "Expected subscription to be a GraphQL type."
+                )
+
+    def describe_a_schema_must_contain_uniquely_named_types():
+        def rejects_a_schema_which_redefines_a_built_in_type():
+            FakeString = GraphQLScalarType("String")
+
+            QueryType = GraphQLObjectType(
+                "Query",
+                {
+                    "normal": GraphQLField(GraphQLString),
+                    "fake": GraphQLField(FakeString),
+                },
+            )
+
+            with raises(TypeError) as exc_info:
+                GraphQLSchema(QueryType)
+            msg = str(exc_info.value)
+            assert msg == (
+                "Schema must contain uniquely named types"
+                " but contains multiple types named 'String'."
+            )
+
+        def rejects_a_schema_when_a_provided_type_has_no_name():
+            query = GraphQLObjectType("Query", {"foo": GraphQLField(GraphQLString)})
+            types = [GraphQLType(), query, GraphQLType()]
+
+            with raises(TypeError) as exc_info:
+                GraphQLSchema(query, types=types)  # type: ignore
+            msg = str(exc_info.value)
+            assert msg == (
+                "One of the provided types for building the Schema is missing a name."
+            )
+
+        def rejects_a_schema_which_defines_an_object_twice():
+            types = [
+                GraphQLObjectType("SameName", {}),
+                GraphQLObjectType("SameName", {}),
+            ]
+
+            with raises(TypeError) as exc_info:
+                GraphQLSchema(types=types)
+            msg = str(exc_info.value)
+            assert msg == (
+                "Schema must contain uniquely named types"
+                " but contains multiple types named 'SameName'."
+            )
+
+        def rejects_a_schema_which_defines_fields_with_conflicting_types():
+            fields: GraphQLFieldMap = {}
+            QueryType = GraphQLObjectType(
+                "Query",
+                {
+                    "a": GraphQLField(GraphQLObjectType("SameName", fields)),
+                    "b": GraphQLField(GraphQLObjectType("SameName", fields)),
+                },
+            )
+
+            with raises(TypeError) as exc_info:
+                GraphQLSchema(QueryType)
+            msg = str(exc_info.value)
+            assert msg == (
+                "Schema must contain uniquely named types"
+                " but contains multiple types named 'SameName'."
+            )
+
+        def describe_when_assumed_valid():
+            def configures_the_schema_to_have_no_errors():
+                # noinspection PyProtectedMember
+                assert GraphQLSchema(assume_valid=True).validation_errors == []
+
+    def describe_ast_nodes():
+        def accepts_a_scalar_type_with_ast_node_and_extension_ast_nodes():
+            ast_node = SchemaDefinitionNode()
+            extension_ast_nodes = [SchemaExtensionNode()]
+            schema = GraphQLSchema(
+                GraphQLObjectType("Query", {}),
+                ast_node=ast_node,
+                extension_ast_nodes=extension_ast_nodes,
+            )
+            assert schema.ast_node is ast_node
+            assert isinstance(schema.extension_ast_nodes, FrozenList)
+            assert schema.extension_ast_nodes == extension_ast_nodes
+            extension_ast_nodes = schema.extension_ast_nodes
+            schema = GraphQLSchema(
+                GraphQLObjectType("Query", {}),
+                ast_node=None,
+                extension_ast_nodes=extension_ast_nodes,
+            )
+            assert schema.ast_node is None
+            assert schema.extension_ast_nodes is extension_ast_nodes
+
+        def rejects_a_schema_with_an_incorrect_ast_node():
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                GraphQLSchema(
+                    GraphQLObjectType("Query", {}),
+                    ast_node=TypeDefinitionNode(),  # type: ignore
+                )
+            msg = str(exc_info.value)
+            assert msg == "Schema AST node must be a SchemaDefinitionNode."
+
+        def rejects_a_scalar_type_with_incorrect_extension_ast_nodes():
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                GraphQLSchema(
+                    GraphQLObjectType("Query", {}),
+                    extension_ast_nodes=[TypeExtensionNode()],  # type: ignore
+                )
+            assert str(exc_info.value) == (
+                "Schema extension AST nodes must be specified"
+                " as a collection of SchemaExtensionNode instances."
+            )
diff --git a/tests/type/test_serialization.py b/tests/type/test_serialization.py
new file mode 100644
index 0000000..5c637b5
--- /dev/null
+++ b/tests/type/test_serialization.py
@@ -0,0 +1,214 @@
+from math import inf, nan
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.type import (
+    GraphQLBoolean,
+    GraphQLFloat,
+    GraphQLID,
+    GraphQLInt,
+    GraphQLString,
+)
+
+
+def describe_type_system_scalar_coercion():
+    def serializes_output_as_int():
+        assert GraphQLInt.serialize(1) == 1
+        assert GraphQLInt.serialize("123") == 123
+        assert GraphQLInt.serialize(0) == 0
+        assert GraphQLInt.serialize(-1) == -1
+        assert GraphQLInt.serialize(1e5) == 100000
+        assert GraphQLInt.serialize(False) == 0
+        assert GraphQLInt.serialize(True) == 1
+        assert GraphQLInt.serialize(type("Int", (int,), {})(5)) == 5
+
+        # The GraphQL specification does not allow serializing non-integer
+        # values as Int to avoid accidental data loss.
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(0.1)
+        assert str(exc_info.value) == "Int cannot represent non-integer value: 0.1"
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(1.1)
+        assert str(exc_info.value) == "Int cannot represent non-integer value: 1.1"
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(-1.1)
+        assert str(exc_info.value) == "Int cannot represent non-integer value: -1.1"
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize("-1.1")
+        assert str(exc_info.value) == "Int cannot represent non-integer value: '-1.1'"
+        # Maybe a safe JavaScript int, but bigger than 2^32, so not
+        # representable as a GraphQL Int
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(9876504321)
+        assert str(exc_info.value) == (
+            "Int cannot represent non 32-bit signed integer value: 9876504321"
+        )
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(-9876504321)
+        assert str(exc_info.value) == (
+            "Int cannot represent non 32-bit signed integer value: -9876504321"
+        )
+        # Too big to represent as an Int in JavaScript or GraphQL
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(1e100)
+        assert str(exc_info.value) == (
+            "Int cannot represent non 32-bit signed integer value: 1e+100"
+        )
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(-1e100)
+        assert str(exc_info.value) == (
+            "Int cannot represent non 32-bit signed integer value: -1e+100"
+        )
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize("one")
+        assert str(exc_info.value) == "Int cannot represent non-integer value: 'one'"
+        # Doesn't represent number
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize("")
+        assert str(exc_info.value) == "Int cannot represent non-integer value: ''"
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(nan)
+        assert str(exc_info.value) == "Int cannot represent non-integer value: nan"
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize(inf)
+        assert str(exc_info.value) == "Int cannot represent non-integer value: inf"
+        with raises(GraphQLError) as exc_info:
+            GraphQLInt.serialize([5])
+        assert str(exc_info.value) == "Int cannot represent non-integer value: [5]"
+
+    def serializes_output_as_float():
+        assert GraphQLFloat.serialize(1) == 1.0
+        assert GraphQLFloat.serialize(0) == 0.0
+        assert GraphQLFloat.serialize("123.5") == 123.5
+        assert GraphQLFloat.serialize(-1) == -1.0
+        assert GraphQLFloat.serialize(0.1) == 0.1
+        assert GraphQLFloat.serialize(1.1) == 1.1
+        assert GraphQLFloat.serialize(-1.1) == -1.1
+        assert GraphQLFloat.serialize("-1.1") == -1.1
+        assert GraphQLFloat.serialize(False) == 0
+        assert GraphQLFloat.serialize(True) == 1
+        assert GraphQLFloat.serialize(type("Float", (float,), {})(5.5)) == 5.5
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLFloat.serialize(nan)
+        assert str(exc_info.value) == "Float cannot represent non numeric value: nan"
+        with raises(GraphQLError) as exc_info:
+            GraphQLFloat.serialize(inf)
+        assert str(exc_info.value) == "Float cannot represent non numeric value: inf"
+        with raises(GraphQLError) as exc_info:
+            GraphQLFloat.serialize("one")
+        assert str(exc_info.value) == (
+            "Float cannot represent non numeric value: 'one'"
+        )
+        with raises(GraphQLError) as exc_info:
+            GraphQLFloat.serialize("")
+        assert str(exc_info.value) == "Float cannot represent non numeric value: ''"
+        with raises(GraphQLError) as exc_info:
+            GraphQLFloat.serialize([5])
+        assert str(exc_info.value) == "Float cannot represent non numeric value: [5]"
+
+    def serializes_output_as_string():
+        assert GraphQLString.serialize("string") == "string"
+        assert GraphQLString.serialize(1) == "1"
+        assert GraphQLString.serialize(-1.1) == "-1.1"
+        assert GraphQLString.serialize(True) == "true"
+        assert GraphQLString.serialize(False) == "false"
+
+        class StringableObjValue:
+            def __str__(self):
+                return "something useful"
+
+        assert GraphQLString.serialize(StringableObjValue()) == "something useful"
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLString.serialize(nan)
+        assert str(exc_info.value) == "String cannot represent value: nan"
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLString.serialize([1])
+        assert str(exc_info.value) == "String cannot represent value: [1]"
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLString.serialize({})
+        assert str(exc_info.value) == "String cannot represent value: {}"
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLString.serialize({"value_of": "value_of string"})
+        assert (
+            str(exc_info.value) == "String cannot represent value:"
+            " {'value_of': 'value_of string'}"
+        )
+
+    def serializes_output_as_boolean():
+        assert GraphQLBoolean.serialize(1) is True
+        assert GraphQLBoolean.serialize(0) is False
+        assert GraphQLBoolean.serialize(True) is True
+        assert GraphQLBoolean.serialize(False) is False
+        with raises(TypeError, match="not an acceptable base type"):
+            # you can't subclass bool in Python
+            assert GraphQLBoolean.serialize(type("Boolean", (bool,), {})(True)) is True
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLBoolean.serialize(nan)
+        assert str(exc_info.value) == (
+            "Boolean cannot represent a non boolean value: nan"
+        )
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLBoolean.serialize("")
+        assert str(exc_info.value) == (
+            "Boolean cannot represent a non boolean value: ''"
+        )
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLBoolean.serialize("True")
+        assert str(exc_info.value) == (
+            "Boolean cannot represent a non boolean value: 'True'"
+        )
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLBoolean.serialize([False])
+        assert str(exc_info.value) == (
+            "Boolean cannot represent a non boolean value: [False]"
+        )
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLBoolean.serialize({})
+        assert str(exc_info.value) == (
+            "Boolean cannot represent a non boolean value: {}"
+        )
+
+    def serializes_output_as_id():
+        assert GraphQLID.serialize("string") == "string"
+        assert GraphQLID.serialize("false") == "false"
+        assert GraphQLID.serialize("") == ""
+        assert GraphQLID.serialize(123) == "123"
+        assert GraphQLID.serialize(0) == "0"
+        assert GraphQLID.serialize(-1) == "-1"
+
+        class ObjValue:
+            def __init__(self, value):
+                self._id = value
+
+            def __str__(self):
+                return str(self._id)
+
+        obj_value = ObjValue(123)
+        assert GraphQLID.serialize(obj_value) == "123"
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLID.serialize(True)
+        assert str(exc_info.value) == "ID cannot represent value: True"
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLID.serialize(3.14)
+        assert str(exc_info.value) == "ID cannot represent value: 3.14"
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLID.serialize({})
+        assert str(exc_info.value) == "ID cannot represent value: {}"
+
+        with raises(GraphQLError) as exc_info:
+            GraphQLID.serialize(["abc"])
+        assert str(exc_info.value) == "ID cannot represent value: ['abc']"
diff --git a/tests/type/test_validation.py b/tests/type/test_validation.py
new file mode 100644
index 0000000..0199791
--- /dev/null
+++ b/tests/type/test_validation.py
@@ -0,0 +1,2660 @@
+from operator import attrgetter
+from typing import Any, List, Union
+
+from pytest import mark, raises  # type: ignore
+
+from graphql.language import parse, DirectiveLocation
+from graphql.pyutils import inspect
+from graphql.type import (
+    assert_directive,
+    assert_enum_type,
+    assert_input_object_type,
+    assert_interface_type,
+    assert_object_type,
+    assert_scalar_type,
+    assert_union_type,
+    assert_valid_schema,
+    is_input_type,
+    is_output_type,
+    validate_schema,
+    GraphQLArgument,
+    GraphQLDirective,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLInputType,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNamedType,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLOutputType,
+    GraphQLSchema,
+    GraphQLString,
+    GraphQLUnionType,
+)
+from graphql.utilities import build_schema, extend_schema
+
+from ..utils import dedent
+
+SomeSchema = build_schema(
+    """
+    scalar SomeScalar
+
+    interface SomeInterface { f: SomeObject }
+
+    type SomeObject implements SomeInterface { f: SomeObject }
+
+    union SomeUnion = SomeObject
+
+    enum SomeEnum { ONLY }
+
+    input SomeInputObject { val: String = "hello" }
+
+    directive @SomeDirective on QUERY
+    """
+)
+
+get_type = SomeSchema.get_type
+SomeScalarType = assert_scalar_type(get_type("SomeScalar"))
+SomeInterfaceType = assert_interface_type(get_type("SomeInterface"))
+SomeObjectType = assert_object_type(get_type("SomeObject"))
+SomeUnionType = assert_union_type(get_type("SomeUnion"))
+SomeEnumType = assert_enum_type(get_type("SomeEnum"))
+SomeInputObjectType = assert_input_object_type(get_type("SomeInputObject"))
+SomeDirective = assert_directive(SomeSchema.get_directive("SomeDirective"))
+
+
+def with_modifiers(
+    type_: GraphQLNamedType,
+) -> List[Union[GraphQLNamedType, GraphQLNonNull, GraphQLList]]:
+    return [
+        type_,
+        GraphQLList(type_),
+        GraphQLNonNull(type_),
+        GraphQLNonNull(GraphQLList(type_)),
+    ]
+
+
+output_types = [
+    *with_modifiers(GraphQLString),
+    *with_modifiers(SomeScalarType),
+    *with_modifiers(SomeEnumType),
+    *with_modifiers(SomeObjectType),
+    *with_modifiers(SomeUnionType),
+    *with_modifiers(SomeInterfaceType),
+]
+
+not_output_types = with_modifiers(SomeInputObjectType)
+
+input_types = [
+    *with_modifiers(GraphQLString),
+    *with_modifiers(SomeScalarType),
+    *with_modifiers(SomeEnumType),
+    *with_modifiers(SomeInputObjectType),
+]
+
+not_input_types = [
+    *with_modifiers(SomeObjectType),
+    *with_modifiers(SomeUnionType),
+    *with_modifiers(SomeInterfaceType),
+]
+
+not_graphql_types = [
+    type("IntType", (int,), {"name": "IntType"}),
+    type("FloatType", (float,), {"name": "FloatType"}),
+    type("StringType", (str,), {"name": "StringType"}),
+]
+
+
+get_name = attrgetter("__class__.__name__")
+
+
+def schema_with_field_type(type_):
+    return GraphQLSchema(
+        query=GraphQLObjectType(name="Query", fields={"f": GraphQLField(type_)}),
+        types=[type_],
+    )
+
+
+def describe_type_system_a_schema_must_have_object_root_types():
+    def accepts_a_schema_whose_query_type_is_an_object_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+        schema_with_def = build_schema(
+            """
+            schema {
+              query: QueryRoot
+            }
+
+            type QueryRoot {
+              test: String
+            }
+            """
+        )
+
+        assert validate_schema(schema_with_def) == []
+
+    def accepts_a_schema_whose_query_and_mutation_types_are_object_types():
+        schema = build_schema(
+            """
+            type Query {
+              test: String
+            }
+
+            type Mutation {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+        schema_with_def = build_schema(
+            """
+            schema {
+              query: QueryRoot
+              mutation: MutationRoot
+            }
+
+            type QueryRoot {
+              test: String
+            }
+
+            type MutationRoot {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema_with_def) == []
+
+    def accepts_a_schema_whose_query_and_subscription_types_are_object_types():
+        schema = build_schema(
+            """
+            type Query {
+              test: String
+            }
+
+            type Subscription {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+        schema_with_def = build_schema(
+            """
+            schema {
+              query: QueryRoot
+              subscription: SubscriptionRoot
+            }
+
+            type QueryRoot {
+              test: String
+            }
+
+            type SubscriptionRoot {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema_with_def) == []
+
+    def rejects_a_schema_without_a_query_type():
+        schema = build_schema(
+            """
+            type Mutation {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {"message": "Query root type must be provided.", "locations": None}
+        ]
+
+        schema_with_def = build_schema(
+            """
+            schema {
+              mutation: MutationRoot
+            }
+
+            type MutationRoot {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema_with_def) == [
+            {"message": "Query root type must be provided.", "locations": [(2, 13)]}
+        ]
+
+    def rejects_a_schema_whose_query_root_type_is_not_an_object_type():
+        schema = build_schema(
+            """
+            input Query {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Query root type must be Object type,"
+                " it cannot be Query.",
+                "locations": [(2, 13)],
+            }
+        ]
+
+        schema_with_def = build_schema(
+            """
+            schema {
+              query: SomeInputObject
+            }
+
+            input SomeInputObject {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema_with_def) == [
+            {
+                "message": "Query root type must be Object type,"
+                " it cannot be SomeInputObject.",
+                "locations": [(3, 22)],
+            }
+        ]
+
+    def rejects_a_schema_whose_mutation_type_is_an_input_type():
+        schema = build_schema(
+            """
+            type Query {
+              field: String
+            }
+
+            input Mutation {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Mutation root type must be Object type if provided,"
+                " it cannot be Mutation.",
+                "locations": [(6, 13)],
+            }
+        ]
+
+        schema_with_def = build_schema(
+            """
+            schema {
+              query: Query
+              mutation: SomeInputObject
+            }
+
+            type Query {
+              field: String
+            }
+
+            input SomeInputObject {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema_with_def) == [
+            {
+                "message": "Mutation root type must be Object type if provided,"
+                " it cannot be SomeInputObject.",
+                "locations": [(4, 25)],
+            }
+        ]
+
+    def rejects_a_schema_whose_subscription_type_is_an_input_type():
+        schema = build_schema(
+            """
+            type Query {
+              field: String
+            }
+
+            input Subscription {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Subscription root type must be Object type if"
+                " provided, it cannot be Subscription.",
+                "locations": [(6, 13)],
+            }
+        ]
+
+        schema_with_def = build_schema(
+            """
+            schema {
+              query: Query
+              subscription: SomeInputObject
+            }
+
+            type Query {
+              field: String
+            }
+
+            input SomeInputObject {
+              test: String
+            }
+            """
+        )
+        assert validate_schema(schema_with_def) == [
+            {
+                "message": "Subscription root type must be Object type if"
+                " provided, it cannot be SomeInputObject.",
+                "locations": [(4, 29)],
+            }
+        ]
+
+    def rejects_a_schema_extended_with_invalid_root_types():
+        schema = build_schema(
+            """
+            input SomeInputObject {
+              test: String
+            }
+            """
+        )
+        schema = extend_schema(
+            schema,
+            parse(
+                """
+                extend schema {
+                  query: SomeInputObject
+                }
+                """
+            ),
+        )
+        schema = extend_schema(
+            schema,
+            parse(
+                """
+                extend schema {
+                  mutation: SomeInputObject
+                }
+                """
+            ),
+        )
+        schema = extend_schema(
+            schema,
+            parse(
+                """
+                extend schema {
+                  subscription: SomeInputObject
+                }
+                """
+            ),
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Query root type must be Object type,"
+                " it cannot be SomeInputObject.",
+                "locations": [(3, 26)],
+            },
+            {
+                "message": "Mutation root type must be Object type"
+                " if provided, it cannot be SomeInputObject.",
+                "locations": [(3, 29)],
+            },
+            {
+                "message": "Subscription root type must be Object type"
+                " if provided, it cannot be SomeInputObject.",
+                "locations": [(3, 33)],
+            },
+        ]
+
+    def rejects_a_schema_whose_types_are_incorrectly_type():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            GraphQLSchema(
+                SomeObjectType,
+                types=[{"name": "SomeType"}, SomeDirective],  # type: ignore
+            )
+        assert str(exc_info.value) == (
+            "Schema types must be specified as a collection of GraphQL types."
+        )
+        # construct invalid schema manually
+        schema = GraphQLSchema(SomeObjectType)
+        schema.type_map = {
+            "SomeType": {"name": "SomeType"},  # type: ignore
+            "SomeDirective": SomeDirective,  # type: ignore
+        }
+        assert validate_schema(schema) == [
+            {"message": "Expected GraphQL named type but got: {'name': 'SomeType'}."},
+            {"message": "Expected GraphQL named type but got: @SomeDirective."},
+        ]
+
+    def rejects_a_schema_whose_directives_are_incorrectly_typed():
+        schema = GraphQLSchema(
+            SomeObjectType,
+            directives=[None, "SomeDirective", SomeScalarType],  # type: ignore
+        )
+        assert validate_schema(schema) == [
+            {"message": "Expected directive but got: None."},
+            {"message": "Expected directive but got: 'SomeDirective'."},
+            {"message": "Expected directive but got: SomeScalar."},
+        ]
+
+
+def describe_type_system_objects_must_have_fields():
+    def accepts_an_object_type_with_fields_object():
+        schema = build_schema(
+            """
+            type Query {
+              field: SomeObject
+            }
+
+            type SomeObject {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_object_type_with_missing_fields():
+        schema = build_schema(
+            """
+            type Query {
+              test: IncompleteObject
+            }
+
+            type IncompleteObject
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Type IncompleteObject must define one or more fields.",
+                "locations": [(6, 13)],
+            }
+        ]
+
+        manual_schema = schema_with_field_type(
+            GraphQLObjectType("IncompleteObject", {})
+        )
+        msg = validate_schema(manual_schema)[0].message
+        assert msg == "Type IncompleteObject must define one or more fields."
+
+        manual_schema_2 = schema_with_field_type(
+            GraphQLObjectType("IncompleteObject", lambda: {})
+        )
+        msg = validate_schema(manual_schema_2)[0].message
+        assert msg == "Type IncompleteObject must define one or more fields."
+
+    def rejects_an_object_type_with_incorrectly_named_fields():
+        schema = schema_with_field_type(
+            GraphQLObjectType(
+                "SomeObject", {"bad-name-with-dashes": GraphQLField(GraphQLString)}
+            )
+        )
+        msg = validate_schema(schema)[0].message
+        assert msg == (
+            "Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/"
+            " but 'bad-name-with-dashes' does not."
+        )
+
+
+def describe_type_system_field_args_must_be_properly_named():
+    def accepts_field_args_with_valid_names():
+        schema = schema_with_field_type(
+            GraphQLObjectType(
+                "SomeObject",
+                {
+                    "goodField": GraphQLField(
+                        GraphQLString, args={"goodArg": GraphQLArgument(GraphQLString)}
+                    )
+                },
+            )
+        )
+        assert validate_schema(schema) == []
+
+    def reject_field_args_with_invalid_names():
+        QueryType = GraphQLObjectType(
+            "SomeObject",
+            {
+                "badField": GraphQLField(
+                    GraphQLString,
+                    args={"bad-name-with-dashes": GraphQLArgument(GraphQLString)},
+                )
+            },
+        )
+        schema = GraphQLSchema(QueryType)
+        msg = validate_schema(schema)[0].message
+        assert msg == (
+            "Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/"
+            " but 'bad-name-with-dashes' does not."
+        )
+
+
+def describe_type_system_union_types_must_be_valid():
+    def accepts_a_union_type_with_member_types():
+        schema = build_schema(
+            """
+            type Query {
+              test: GoodUnion
+            }
+
+            type TypeA {
+              field: String
+            }
+
+            type TypeB {
+              field: String
+            }
+
+            union GoodUnion =
+              | TypeA
+              | TypeB
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_a_union_type_with_empty_types():
+        schema = build_schema(
+            """
+            type Query {
+              test: BadUnion
+            }
+
+            union BadUnion
+            """
+        )
+
+        schema = extend_schema(
+            schema,
+            parse(
+                """
+                directive @test on UNION
+
+                extend union BadUnion @test
+                """
+            ),
+        )
+
+        assert validate_schema(schema) == [
+            {
+                "message": "Union type BadUnion must define one or more member types.",
+                "locations": [(6, 13), (4, 17)],
+            }
+        ]
+
+    def rejects_a_union_type_with_duplicated_member_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: BadUnion
+            }
+
+            type TypeA {
+              field: String
+            }
+
+            type TypeB {
+              field: String
+            }
+
+            union BadUnion =
+              | TypeA
+              | TypeB
+              | TypeA
+            """
+        )
+
+        assert validate_schema(schema) == [
+            {
+                "message": "Union type BadUnion can only include type TypeA once.",
+                "locations": [(15, 17), (17, 17)],
+            }
+        ]
+
+        schema = extend_schema(schema, parse("extend union BadUnion = TypeB"))
+
+        assert validate_schema(schema) == [
+            {
+                "message": "Union type BadUnion can only include type TypeA once.",
+                "locations": [(15, 17), (17, 17)],
+            },
+            {
+                "message": "Union type BadUnion can only include type TypeB once.",
+                "locations": [(16, 17), (1, 25)],
+            },
+        ]
+
+    def rejects_a_union_type_with_non_object_member_types():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                type Query {
+                  test: BadUnion
+                }
+
+                type TypeA {
+                  field: String
+                }
+
+                type TypeB {
+                  field: String
+                }
+
+                union BadUnion =
+                  | TypeA
+                  | String
+                  | TypeB
+                """
+            )
+        assert str(exc_info.value) == (
+            "BadUnion types must be specified"
+            " as a collection of GraphQLObjectType instances."
+        )
+        # construct invalid schema manually
+        schema = build_schema(
+            """
+            type Query {
+              test: BadUnion
+            }
+
+            type TypeA {
+              field: String
+            }
+
+            type TypeB {
+              field: String
+            }
+
+            union BadUnion =
+              | TypeA
+              | TypeA
+              | TypeB
+            """
+        )
+        with raises(TypeError) as exc_info:
+            extend_schema(schema, parse("extend union BadUnion = Int"))
+        assert str(exc_info.value) == (
+            "BadUnion types must be specified"
+            " as a collection of GraphQLObjectType instances."
+        )
+        schema = extend_schema(schema, parse("extend union BadUnion = TypeB"))
+        bad_union: Any = schema.get_type("BadUnion")
+        assert bad_union.types[1].name == "TypeA"
+        bad_union.types[1] = GraphQLString
+        assert bad_union.types[3].name == "TypeB"
+        bad_union.types[3] = GraphQLInt
+        bad_union.ast_node.types[1].name.value = "String"
+        bad_union.extension_ast_nodes[0].types[0].name.value = "Int"
+        assert validate_schema(schema) == [
+            {
+                "message": "Union type BadUnion can only include Object types,"
+                " it cannot include String.",
+                "locations": [(16, 17)],
+            },
+            {
+                "message": "Union type BadUnion can only include Object types,"
+                " it cannot include Int.",
+                "locations": [(1, 25)],
+            },
+        ]
+
+        bad_union_member_types = [
+            GraphQLString,
+            GraphQLNonNull(SomeObjectType),
+            GraphQLList(SomeObjectType),
+            SomeInterfaceType,
+            SomeUnionType,
+            SomeEnumType,
+            SomeInputObjectType,
+        ]
+        for member_type in bad_union_member_types:
+            # invalid union type cannot be built with Python
+            bad_union = GraphQLUnionType(
+                "BadUnion", types=[member_type]  # type: ignore
+            )
+            with raises(TypeError) as exc_info:
+                schema_with_field_type(bad_union)
+            assert str(exc_info.value) == (
+                "BadUnion types must be specified"
+                " as a collection of GraphQLObjectType instances."
+            )
+            # noinspection PyPropertyAccess
+            bad_union.types = []
+            bad_schema = schema_with_field_type(bad_union)
+            # noinspection PyPropertyAccess
+            bad_union.types = [member_type]
+            assert validate_schema(bad_schema) == [
+                {
+                    "message": "Union type BadUnion can only include Object types,"
+                    + f" it cannot include {inspect(member_type)}."
+                }
+            ]
+
+
+def describe_type_system_input_objects_must_have_fields():
+    def accepts_an_input_object_type_with_fields():
+        schema = build_schema(
+            """
+            type Query {
+               field(arg: SomeInputObject): String
+            }
+
+            input SomeInputObject {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_input_object_type_with_missing_fields():
+        schema = build_schema(
+            """
+            type Query {
+              field(arg: SomeInputObject): String
+            }
+
+            input SomeInputObject
+            """
+        )
+        schema = extend_schema(
+            schema,
+            parse(
+                """
+                directive @test on INPUT_OBJECT
+
+                extend input SomeInputObject @test
+                """
+            ),
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Input Object type SomeInputObject"
+                " must define one or more fields.",
+                "locations": [(6, 13), (4, 17)],
+            }
+        ]
+
+    def accepts_an_input_object_with_breakable_circular_reference():
+        schema = build_schema(
+            """
+            type Query {
+              field(arg: SomeInputObject): String
+            }
+
+            input SomeInputObject {
+              self: SomeInputObject
+              arrayOfSelf: [SomeInputObject]
+              nonNullArrayOfSelf: [SomeInputObject]!
+              nonNullArrayOfNonNullSelf: [SomeInputObject!]!
+              intermediateSelf: AnotherInputObject
+            }
+
+            input AnotherInputObject {
+              parent: SomeInputObject
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_input_object_with_non_breakable_circular_reference():
+        schema = build_schema(
+            """
+            type Query {
+              field(arg: SomeInputObject): String
+            }
+
+            input SomeInputObject {
+              startLoop: AnotherInputObject!
+            }
+
+            input AnotherInputObject {
+              nextInLoop: YetAnotherInputObject!
+            }
+
+            input YetAnotherInputObject {
+              closeLoop: SomeInputObject!
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Cannot reference Input Object 'SomeInputObject'"
+                " within itself through a series of non-null fields:"
+                " 'startLoop.nextInLoop.closeLoop'.",
+                "locations": [(7, 15), (11, 15), (15, 15)],
+            }
+        ]
+
+    def rejects_an_input_object_with_multiple_non_breakable_circular_reference():
+        schema = build_schema(
+            """
+            type Query {
+              field(arg: SomeInputObject): String
+            }
+
+            input SomeInputObject {
+              startLoop: AnotherInputObject!
+            }
+
+            input AnotherInputObject {
+              closeLoop: SomeInputObject!
+              startSecondLoop: YetAnotherInputObject!
+            }
+
+            input YetAnotherInputObject {
+              closeSecondLoop: AnotherInputObject!
+              nonNullSelf: YetAnotherInputObject!
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Cannot reference Input Object 'SomeInputObject'"
+                " within itself through a series of non-null fields:"
+                " 'startLoop.closeLoop'.",
+                "locations": [(7, 15), (11, 15)],
+            },
+            {
+                "message": "Cannot reference Input Object 'AnotherInputObject'"
+                " within itself through a series of non-null fields:"
+                " 'startSecondLoop.closeSecondLoop'.",
+                "locations": [(12, 15), (16, 15)],
+            },
+            {
+                "message": "Cannot reference Input Object 'YetAnotherInputObject'"
+                " within itself through a series of non-null fields:"
+                " 'nonNullSelf'.",
+                "locations": [(17, 15)],
+            },
+        ]
+
+    def rejects_an_input_object_type_with_incorrectly_typed_fields():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                type Query {
+                  field(arg: SomeInputObject): String
+                }
+
+                type SomeObject {
+                  field: String
+                }
+
+                union SomeUnion = SomeObject
+
+                input SomeInputObject {
+                  badObject: SomeObject
+                  badUnion: SomeUnion
+                  goodInputObject: SomeInputObject
+                }
+                """
+            )
+        assert str(exc_info.value) == (
+            "SomeInputObject fields cannot be resolved."
+            " Input field type must be a GraphQL input type."
+        )
+        # construct invalid schema manually
+        schema = build_schema(
+            """
+            type Query {
+              field(arg: SomeInputObject): String
+            }
+
+            type SomeObject {
+              field: String
+            }
+
+            union SomeUnion = SomeObject
+
+            input SomeInputObject {
+              badObject: SomeInputObject
+              badUnion: SomeInputObject
+              goodInputObject: SomeInputObject
+            }
+            """
+        )
+        some_input_obj: Any = schema.get_type("SomeInputObject")
+        some_input_obj.fields["badObject"].type = schema.get_type("SomeObject")
+        some_input_obj.fields["badUnion"].type = schema.get_type("SomeUnion")
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of SomeInputObject.badObject must be Input Type"
+                " but got: SomeObject.",
+                "locations": [(13, 26)],
+            },
+            {
+                "message": "The type of SomeInputObject.badUnion must be Input Type"
+                " but got: SomeUnion.",
+                "locations": [(14, 25)],
+            },
+        ]
+
+
+def describe_type_system_enum_types_must_be_well_defined():
+    def rejects_an_enum_type_without_values():
+        schema = build_schema(
+            """
+            type Query {
+              field: SomeEnum
+            }
+
+            enum SomeEnum
+            """
+        )
+
+        schema = extend_schema(
+            schema,
+            parse(
+                """
+                directive @test on ENUM
+
+                extend enum SomeEnum @test
+                """
+            ),
+        )
+
+        assert validate_schema(schema) == [
+            {
+                "message": "Enum type SomeEnum must define one or more values.",
+                "locations": [(6, 13), (4, 17)],
+            }
+        ]
+
+    def rejects_an_enum_type_with_incorrectly_named_values():
+        def schema_with_enum(name):
+            return schema_with_field_type(
+                GraphQLEnumType("SomeEnum", {name: GraphQLEnumValue(1)})
+            )
+
+        schema1 = schema_with_enum("#value")
+        msg = validate_schema(schema1)[0].message
+        assert msg == (
+            "Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but '#value' does not."
+        )
+
+        schema2 = schema_with_enum("1value")
+        msg = validate_schema(schema2)[0].message
+        assert msg == (
+            "Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but '1value' does not."
+        )
+
+        schema3 = schema_with_enum("KEBAB-CASE")
+        msg = validate_schema(schema3)[0].message
+        assert msg == (
+            "Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but 'KEBAB-CASE' does not."
+        )
+
+        schema4 = schema_with_enum("true")
+        msg = validate_schema(schema4)[0].message
+        assert msg == "Enum type SomeEnum cannot include value: true."
+
+        schema5 = schema_with_enum("false")
+        msg = validate_schema(schema5)[0].message
+        assert msg == "Enum type SomeEnum cannot include value: false."
+
+        schema6 = schema_with_enum("null")
+        msg = validate_schema(schema6)[0].message
+        assert msg == "Enum type SomeEnum cannot include value: null."
+
+
+def describe_type_system_object_fields_must_have_output_types():
+    def _schema_with_object_field_of_type(field_type: GraphQLOutputType):
+        if is_output_type(field_type):
+            field = GraphQLField(field_type)
+        else:
+            # invalid field cannot be built with Python directly
+            with raises(TypeError) as exc_info:
+                GraphQLField(field_type)
+            assert str(exc_info.value) == "Field type must be an output type."
+            # therefore we need to monkey-patch a valid field
+            field = GraphQLField(GraphQLString)
+            field.type = field_type
+        bad_object_type = GraphQLObjectType("BadObject", {"badField": field})
+        return GraphQLSchema(
+            GraphQLObjectType("Query", {"f": GraphQLField(bad_object_type)}),
+            types=[SomeObjectType],
+        )
+
+    @mark.parametrize("type_", output_types, ids=get_name)
+    def accepts_an_output_type_as_an_object_field_type(type_):
+        schema = _schema_with_object_field_of_type(type_)
+        assert validate_schema(schema) == []
+
+    def rejects_an_empty_object_field_type():
+        # noinspection PyTypeChecker
+        schema = _schema_with_object_field_of_type(None)  # type: ignore
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadObject.badField must be Output Type"
+                " but got: None."
+            }
+        ]
+
+    @mark.parametrize("type_", not_output_types, ids=get_name)
+    def rejects_a_non_output_type_as_an_object_field_type(type_):
+        schema = _schema_with_object_field_of_type(type_)
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadObject.badField must be Output Type"
+                f" but got: {type_}."
+            }
+        ]
+
+    @mark.parametrize("type_", not_graphql_types, ids=get_name)
+    def rejects_a_non_type_value_as_an_object_field_type(type_):
+        schema = _schema_with_object_field_of_type(type_)
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadObject.badField must be Output Type"
+                f" but got: {inspect(type_)}.",
+            },
+            {"message": f"Expected GraphQL named type but got: {inspect(type_)}."},
+        ]
+
+    def rejects_with_relevant_locations_for_a_non_output_type():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                type Query {
+                  field: [SomeInputObject]
+                }
+
+                input SomeInputObject {
+                  field: String
+                }
+                """
+            )
+        assert str(exc_info.value) == (
+            "Query fields cannot be resolved. Field type must be an output type."
+        )
+        # therefore we need to monkey-patch a valid schema
+        schema = build_schema(
+            """
+            type Query {
+              field: [String]
+            }
+
+            input SomeInputObject {
+              field: String
+            }
+            """
+        )
+        some_input_obj = schema.get_type("SomeInputObject")
+        schema.query_type.fields["field"].type.of_type = some_input_obj  # type: ignore
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of Query.field must be Output Type"
+                " but got: [SomeInputObject].",
+                "locations": [(3, 22)],
+            }
+        ]
+
+
+def describe_type_system_objects_can_only_implement_unique_interfaces():
+    def rejects_an_object_implementing_a_non_type_values():
+        query_type = GraphQLObjectType(
+            "BadObject", {"f": GraphQLField(GraphQLString)}, interfaces=[]
+        )
+        # noinspection PyTypeChecker
+        query_type.interfaces.append(None)
+        schema = GraphQLSchema(query_type)
+
+        assert validate_schema(schema) == [
+            {
+                "message": "Type BadObject must only implement Interface types,"
+                " it cannot implement None."
+            }
+        ]
+
+    def rejects_an_object_implementing_a_non_interface_type():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                type Query {
+                  test: BadObject
+                }
+
+                input SomeInputObject {
+                  field: String
+                }
+
+                type BadObject implements SomeInputObject {
+                  field: String
+                }
+                """
+            )
+        assert str(exc_info.value) == (
+            "BadObject interfaces must be specified"
+            " as a collection of GraphQLInterfaceType instances."
+        )
+
+    def rejects_an_object_implementing_the_same_interface_twice():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: String
+            }
+
+            type AnotherObject implements AnotherInterface & AnotherInterface {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Type AnotherObject can only implement"
+                " AnotherInterface once.",
+                "locations": [(10, 43), (10, 62)],
+            }
+        ]
+
+    def rejects_an_object_implementing_same_interface_twice_due_to_extension():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String
+            }
+            """
+        )
+        extended_schema = extend_schema(
+            schema, parse("extend type AnotherObject implements AnotherInterface")
+        )
+        assert validate_schema(extended_schema) == [
+            {
+                "message": "Type AnotherObject can only implement"
+                " AnotherInterface once.",
+                "locations": [(10, 43), (1, 38)],
+            }
+        ]
+
+
+def describe_type_system_interface_extensions_should_be_valid():
+    def rejects_object_implementing_extended_interface_due_to_missing_field():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String
+            }
+            """
+        )
+        extended_schema = extend_schema(
+            schema,
+            parse(
+                """
+                extend interface AnotherInterface {
+                  newField: String
+                }
+
+                extend type AnotherObject {
+                  differentNewField: String
+                }
+                """
+            ),
+        )
+        assert validate_schema(extended_schema) == [
+            {
+                "message": "Interface field AnotherInterface.newField expected"
+                " but AnotherObject does not provide it.",
+                "locations": [(3, 19), (10, 13), (6, 17)],
+            }
+        ]
+
+    def rejects_object_implementing_extended_interface_due_to_missing_args():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String
+            }
+            """
+        )
+        extended_schema = extend_schema(
+            schema,
+            parse(
+                """
+                extend interface AnotherInterface {
+                  newField(test: Boolean): String
+                }
+
+                extend type AnotherObject {
+                  newField: String
+                }
+                """
+            ),
+        )
+        assert validate_schema(extended_schema) == [
+            {
+                "message": "Interface field argument"
+                " AnotherInterface.newField(test:) expected"
+                " but AnotherObject.newField does not provide it.",
+                "locations": [(3, 28), (7, 19)],
+            }
+        ]
+
+    def rejects_object_implementing_extended_interface_due_to_type_mismatch():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String
+            }
+            """
+        )
+        extended_schema = extend_schema(
+            schema,
+            parse(
+                """
+                extend interface AnotherInterface {
+                  newInterfaceField: NewInterface
+                }
+
+                interface NewInterface {
+                  newField: String
+                }
+
+                interface MismatchingInterface {
+                  newField: String
+                }
+
+                extend type AnotherObject {
+                  newInterfaceField: MismatchingInterface
+                }
+
+                # Required to prevent unused interface errors
+                type DummyObject implements NewInterface & MismatchingInterface {
+                  newField: String
+                }
+                """
+            ),
+        )
+        assert validate_schema(extended_schema) == [
+            {
+                "message": "Interface field AnotherInterface.newInterfaceField"
+                " expects type NewInterface"
+                " but AnotherObject.newInterfaceField"
+                " is type MismatchingInterface.",
+                "locations": [(3, 38), (15, 38)],
+            }
+        ]
+
+
+def describe_type_system_interface_fields_must_have_output_types():
+    def _schema_with_interface_field_of_type(field_type: GraphQLOutputType):
+        if is_output_type(field_type):
+            field = GraphQLField(field_type)
+        else:
+            # invalid field cannot be built with Python directly
+            with raises(TypeError) as exc_info:
+                GraphQLField(field_type)
+            assert str(exc_info.value) == "Field type must be an output type."
+            # therefore we need to monkey-patch a valid field
+            field = GraphQLField(GraphQLString)
+            field.type = field_type
+        bad_interface_type = GraphQLInterfaceType("BadInterface", {"badField": field})
+        bad_implementing_type = GraphQLObjectType(
+            "BadImplementing", {"badField": field}, interfaces=[bad_interface_type],
+        )
+        return GraphQLSchema(
+            GraphQLObjectType("Query", {"f": GraphQLField(bad_interface_type)}),
+            types=[bad_implementing_type, SomeObjectType],
+        )
+
+    @mark.parametrize("type_", output_types, ids=get_name)
+    def accepts_an_output_type_as_an_interface_field_type(type_):
+        schema = _schema_with_interface_field_of_type(type_)
+        assert validate_schema(schema) == []
+
+    def rejects_an_empty_interface_field_type():
+        # noinspection PyTypeChecker
+        schema = _schema_with_interface_field_of_type(None)  # type: ignore
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadImplementing.badField must be Output Type"
+                " but got: None.",
+            },
+            {
+                "message": "The type of BadInterface.badField must be Output Type"
+                " but got: None.",
+            },
+        ]
+
+    @mark.parametrize("type_", not_output_types, ids=get_name)
+    def rejects_a_non_output_type_as_an_interface_field_type(type_):
+        schema = _schema_with_interface_field_of_type(type_)
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadImplementing.badField must be Output Type"
+                f" but got: {type_}.",
+            },
+            {
+                "message": "The type of BadInterface.badField must be Output Type"
+                f" but got: {type_}.",
+            },
+        ]
+
+    @mark.parametrize("type_", not_graphql_types, ids=get_name)
+    def rejects_a_non_type_value_as_an_interface_field_type(type_):
+        schema = _schema_with_interface_field_of_type(type_)
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadImplementing.badField must be Output Type"
+                f" but got: {inspect(type_)}.",
+            },
+            {
+                "message": "The type of BadInterface.badField must be Output Type"
+                f" but got: {inspect(type_)}.",
+            },
+            {"message": f"Expected GraphQL named type but got: {inspect(type_)}."},
+        ]
+
+    def rejects_a_non_output_type_as_an_interface_field_with_locations():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                type Query {
+                  test: SomeInterface
+                }
+
+                interface SomeInterface {
+                  field: SomeInputObject
+                }
+
+                input SomeInputObject {
+                  foo: String
+                }
+
+                type SomeObject implements SomeInterface {
+                  field: SomeInputObject
+                }
+                """
+            )
+        assert str(exc_info.value) == (
+            "SomeInterface fields cannot be resolved."
+            " Field type must be an output type."
+        )
+        # therefore we need to monkey-patch a valid schema
+        schema = build_schema(
+            """
+            type Query {
+              test: SomeInterface
+            }
+
+            interface SomeInterface {
+              field: String
+            }
+
+            input SomeInputObject {
+              foo: String
+            }
+
+            type SomeObject implements SomeInterface {
+              field: String
+            }
+            """
+        )
+        # therefore we need to monkey-patch a valid schema
+        some_input_obj = schema.get_type("SomeInputObject")
+        some_interface: Any = schema.get_type("SomeInterface")
+        some_interface.fields["field"].type = some_input_obj
+        some_object: Any = schema.get_type("SomeObject")
+        some_object.fields["field"].type = some_input_obj
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of SomeInterface.field must be Output Type"
+                " but got: SomeInputObject.",
+                "locations": [(7, 22)],
+            },
+            {
+                "message": "The type of SomeObject.field must be Output Type"
+                " but got: SomeInputObject.",
+                "locations": [(15, 22)],
+            },
+        ]
+
+    def accepts_an_interface_not_implemented_by_at_least_one_object():
+        schema = build_schema(
+            """
+            type Query {
+              test: SomeInterface
+            }
+
+            interface SomeInterface {
+              foo: String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+
+def describe_type_system_arguments_must_have_input_types():
+    def _schema_with_arg_of_type(arg_type: GraphQLInputType):
+        if is_input_type(arg_type):
+            argument = GraphQLArgument(arg_type)
+        else:
+            # invalid argument cannot be built with Python directly
+            with raises(TypeError) as exc_info:
+                GraphQLArgument(arg_type)
+            assert str(exc_info.value) == "Argument type must be a GraphQL input type."
+            # therefore we need to monkey-patch a valid argument
+            argument = GraphQLArgument(GraphQLString)
+            argument.type = arg_type
+        bad_object_type = GraphQLObjectType(
+            "BadObject",
+            {"badField": GraphQLField(GraphQLString, args={"badArg": argument})},
+        )
+        return GraphQLSchema(
+            GraphQLObjectType("Query", {"f": GraphQLField(bad_object_type)}),
+            directives=[
+                GraphQLDirective(
+                    "BadDirective", [DirectiveLocation.QUERY], {"badArg": argument},
+                )
+            ],
+        )
+
+    @mark.parametrize("type_", input_types, ids=get_name)
+    def accepts_an_input_type_as_a_field_arg_type(type_):
+        schema = _schema_with_arg_of_type(type_)
+        assert validate_schema(schema) == []
+
+    def rejects_an_empty_field_arg_type():
+        # noinspection PyTypeChecker
+        schema = _schema_with_arg_of_type(None)  # type: ignore
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of @BadDirective(badArg:) must be Input Type"
+                " but got: None."
+            },
+            {
+                "message": "The type of BadObject.badField(badArg:) must be Input Type"
+                " but got: None."
+            },
+        ]
+
+    @mark.parametrize("type_", not_input_types, ids=get_name)
+    def rejects_a_non_input_type_as_a_field_arg_type(type_):
+        schema = _schema_with_arg_of_type(type_)
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of @BadDirective(badArg:) must be Input Type"
+                f" but got: {type_}."
+            },
+            {
+                "message": "The type of BadObject.badField(badArg:) must be Input Type"
+                f" but got: {type_}."
+            },
+        ]
+
+    @mark.parametrize("type_", not_graphql_types, ids=get_name)
+    def rejects_a_non_type_value_as_a_field_arg_type(type_):
+        schema = _schema_with_arg_of_type(type_)
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of @BadDirective(badArg:) must be Input Type"
+                f" but got: {inspect(type_)}."
+            },
+            {
+                "message": "The type of BadObject.badField(badArg:) must be Input Type"
+                f" but got: {inspect(type_)}."
+            },
+            {"message": f"Expected GraphQL named type but got: {inspect(type_)}."},
+        ]
+
+    def rejects_a_non_input_type_as_a_field_arg_with_locations():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                type Query {
+                  test(arg: SomeObject): String
+                }
+
+                type SomeObject {
+                  foo: String
+                }
+                """
+            )
+        assert str(exc_info.value) == (
+            "Query fields cannot be resolved."
+            " Argument type must be a GraphQL input type."
+        )
+        # therefore we need to monkey-patch a valid schema
+        schema = build_schema(
+            """
+            type Query {
+              test(arg: String): String
+            }
+
+            type SomeObject {
+              foo: String
+            }
+            """
+        )
+        some_object = schema.get_type("SomeObject")
+        schema.query_type.fields["test"].args["arg"].type = some_object  # type: ignore
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of Query.test(arg:) must be Input Type"
+                " but got: SomeObject.",
+                "locations": [(3, 25)],
+            },
+        ]
+
+
+def describe_type_system_input_object_fields_must_have_input_types():
+    def _schema_with_input_field_of_type(input_field_type: GraphQLInputType):
+        if is_input_type(input_field_type):
+            input_field = GraphQLInputField(input_field_type)
+        else:
+            # invalid input field cannot be built with Python directly
+            with raises(TypeError) as exc_info:
+                GraphQLInputField(input_field_type)
+            assert str(exc_info.value) == (
+                "Input field type must be a GraphQL input type."
+            )
+            # therefore we need to monkey-patch a valid input field
+            input_field = GraphQLInputField(GraphQLString)
+            input_field.type = input_field_type
+        bad_input_object_type = GraphQLInputObjectType(
+            "BadInputObject", {"badField": input_field}
+        )
+        return GraphQLSchema(
+            GraphQLObjectType(
+                "Query",
+                {
+                    "f": GraphQLField(
+                        GraphQLString,
+                        args={"badArg": GraphQLArgument(bad_input_object_type)},
+                    )
+                },
+            )
+        )
+
+    @mark.parametrize("type_", input_types, ids=get_name)
+    def accepts_an_input_type_as_an_input_field_type(type_):
+        schema = _schema_with_input_field_of_type(type_)
+        assert validate_schema(schema) == []
+
+    def rejects_an_empty_input_field_type():
+        # noinspection PyTypeChecker
+        schema = _schema_with_input_field_of_type(None)  # type: ignore
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadInputObject.badField must be Input Type"
+                " but got: None."
+            }
+        ]
+
+    @mark.parametrize("type_", not_input_types, ids=get_name)
+    def rejects_a_non_input_type_as_an_input_field_type(type_):
+        schema = _schema_with_input_field_of_type(type_)
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadInputObject.badField must be Input Type"
+                f" but got: {type_}."
+            }
+        ]
+
+    @mark.parametrize("type_", not_graphql_types, ids=get_name)
+    def rejects_a_non_type_value_as_an_input_field_type(type_):
+        schema = _schema_with_input_field_of_type(type_)
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of BadInputObject.badField must be Input Type"
+                f" but got: {inspect(type_)}."
+            },
+            {"message": f"Expected GraphQL named type but got: {inspect(type_)}."},
+        ]
+
+    def rejects_with_relevant_locations_for_a_non_input_type():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                type Query {
+                  test(arg: SomeInputObject): String
+                }
+
+                input SomeInputObject {
+                  foo: SomeObject
+                }
+
+                type SomeObject {
+                  bar: String
+                }
+                """
+            )
+        assert str(exc_info.value) == (
+            "SomeInputObject fields cannot be resolved."
+            " Input field type must be a GraphQL input type."
+        )
+        # therefore we need to monkey-patch a valid schema
+        schema = build_schema(
+            """
+            type Query {
+              test(arg: SomeInputObject): String
+            }
+
+            input SomeInputObject {
+              foo: String
+            }
+
+            type SomeObject {
+              bar: String
+            }
+            """
+        )
+        some_object = schema.get_type("SomeObject")
+        some_input_object: Any = schema.get_type("SomeInputObject")
+        some_input_object.fields["foo"].type = some_object
+        assert validate_schema(schema) == [
+            {
+                "message": "The type of SomeInputObject.foo must be Input Type"
+                " but got: SomeObject.",
+                "locations": [(7, 20)],
+            }
+        ]
+
+
+def describe_objects_must_adhere_to_interfaces_they_implement():
+    def accepts_an_object_which_implements_an_interface():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(input: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field(input: String): String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def accepts_an_object_which_implements_an_interface_and_with_more_fields():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(input: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field(input: String): String
+              anotherField: String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def accepts_an_object_which_implements_an_interface_field_with_more_args():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(input: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field(input: String, anotherInput: String): String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_object_missing_an_interface_field():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(input: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              anotherField: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field AnotherInterface.field expected but"
+                " AnotherObject does not provide it.",
+                "locations": [(7, 15), (10, 13)],
+            }
+        ]
+
+    def rejects_an_object_with_an_incorrectly_typed_interface_field():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(input: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field(input: String): Int
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field AnotherInterface.field"
+                " expects type String but"
+                " AnotherObject.field is type Int.",
+                "locations": [(7, 37), (11, 37)],
+            }
+        ]
+
+    def rejects_an_object_with_a_differently_typed_interface_field():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            type A { foo: String }
+            type B { foo: String }
+
+            interface AnotherInterface {
+              field: A
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: B
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field AnotherInterface.field"
+                " expects type A but AnotherObject.field is type B.",
+                "locations": [(10, 22), (14, 22)],
+            }
+        ]
+
+    def accepts_an_object_with_a_subtyped_interface_field_interface():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: AnotherInterface
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: AnotherObject
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def accepts_an_object_with_a_subtyped_interface_field_union():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            type SomeObject {
+              field: String
+            }
+
+            union SomeUnionType = SomeObject
+
+            interface AnotherInterface {
+              field: SomeUnionType
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: SomeObject
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_object_missing_an_interface_argument():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(input: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field argument"
+                " AnotherInterface.field(input:) expected"
+                " but AnotherObject.field does not provide it.",
+                "locations": [(7, 21), (11, 15)],
+            }
+        ]
+
+    def rejects_an_object_with_an_incorrectly_typed_interface_argument():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(input: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field(input: Int): String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field argument"
+                " AnotherInterface.field(input:) expects type String"
+                " but AnotherObject.field(input:) is type Int.",
+                "locations": [(7, 28), (11, 28)],
+            }
+        ]
+
+    def rejects_an_object_with_an_incorrectly_typed_field_and_argument():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(input: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field(input: Int): Int
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field AnotherInterface.field expects"
+                " type String but AnotherObject.field is type Int.",
+                "locations": [(7, 37), (11, 34)],
+            },
+            {
+                "message": "Interface field argument"
+                " AnotherInterface.field(input:) expects type String"
+                " but AnotherObject.field(input:) is type Int.",
+                "locations": [(7, 28), (11, 28)],
+            },
+        ]
+
+    def rejects_object_implementing_an_interface_field_with_additional_args():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field(baseArg: String): String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field(
+                baseArg: String,
+                requiredArg: String!
+                optionalArg1: String,
+                optionalArg2: String = "",
+              ): String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Object field AnotherObject.field includes required"
+                " argument requiredArg that is missing from the"
+                " Interface field AnotherInterface.field.",
+                "locations": [(13, 17), (7, 15)],
+            }
+        ]
+
+    def accepts_an_object_with_an_equivalently_wrapped_interface_field_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: [String]!
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: [String]!
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_object_with_a_non_list_interface_field_list_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: [String]
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field AnotherInterface.field expects type"
+                " [String] but AnotherObject.field is type String.",
+                "locations": [(7, 22), (11, 22)],
+            }
+        ]
+
+    def rejects_an_object_with_a_list_interface_field_non_list_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: [String]
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field AnotherInterface.field expects type"
+                " String but AnotherObject.field is type [String].",
+                "locations": [(7, 22), (11, 22)],
+            }
+        ]
+
+    def accepts_an_object_with_a_subset_non_null_interface_field_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: String
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String!
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_object_with_a_superset_nullable_interface_field_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface AnotherInterface {
+              field: String!
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field AnotherInterface.field expects type"
+                " String! but AnotherObject.field is type String.",
+                "locations": [(7, 22), (11, 22)],
+            }
+        ]
+
+    def rejects_an_object_missing_a_transitive_interface():
+        schema = build_schema(
+            """
+            type Query {
+              test: AnotherObject
+            }
+
+            interface SuperInterface {
+              field: String!
+            }
+
+            interface AnotherInterface implements SuperInterface {
+              field: String!
+            }
+
+            type AnotherObject implements AnotherInterface {
+              field: String!
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Type AnotherObject must implement SuperInterface"
+                " because it is implemented by AnotherInterface.",
+                "locations": [(10, 51), (14, 43)],
+            }
+        ]
+
+
+def describe_interfaces_must_adhere_to_interface_they_implement():
+    def accepts_an_interface_which_implements_an_interface():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(input: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field(input: String): String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def accepts_an_interface_which_implements_an_interface_along_with_more_fields():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(input: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field(input: String): String
+              anotherField: String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def accepts_an_interface_which_implements_an_interface_with_additional_args():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(input: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field(input: String, anotherInput: String): String
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_interface_missing_an_interface_field():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(input: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              anotherField: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field ParentInterface.field expected"
+                " but ChildInterface does not provide it.",
+                "locations": [(7, 15), (10, 13)],
+            }
+        ]
+
+    def rejects_an_interface_with_an_incorrectly_typed_interface_field():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(input: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field(input: String): Int
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field ParentInterface.field expects type String"
+                " but ChildInterface.field is type Int.",
+                "locations": [(7, 37), (11, 37)],
+            }
+        ]
+
+    def rejects_an_interface_with_a_differently_typed_interface_field():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            type A { foo: String }
+            type B { foo: String }
+
+            interface ParentInterface {
+              field: A
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: B
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field ParentInterface.field expects type A"
+                " but ChildInterface.field is type B.",
+                "locations": [(10, 22), (14, 22)],
+            }
+        ]
+
+    def accepts_an_interface_with_a_subtyped_interface_field_interface():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field: ParentInterface
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: ChildInterface
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def accepts_an_interface_with_a_subtyped_interface_field_union():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            type SomeObject {
+              field: String
+            }
+
+            union SomeUnionType = SomeObject
+
+            interface ParentInterface {
+              field: SomeUnionType
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: SomeObject
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_interface_implementing_a_non_interface_type():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                type Query {
+                  field: String
+                }
+
+                input SomeInputObject {
+                  field: String
+                }
+
+                interface BadInterface implements SomeInputObject {
+                  field: String
+                }
+                """
+            )
+        assert str(exc_info.value) == (
+            "BadInterface interfaces must be specified as a collection"
+            " of GraphQLInterfaceType instances."
+        )
+        # therefore we construct the invalid schema manually
+        some_input_obj = GraphQLInputObjectType(
+            "SomeInputObject", {"field": GraphQLInputField(GraphQLString)}
+        )
+        bad_interface = GraphQLInterfaceType(
+            "BadInterface", {"field": GraphQLField(GraphQLString)}
+        )
+        # noinspection PyTypeChecker
+        bad_interface.interfaces.append(some_input_obj)
+        schema = GraphQLSchema(
+            GraphQLObjectType("Query", {"field": GraphQLField(GraphQLString)}),
+            types=[bad_interface],
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Type BadInterface must only implement Interface types,"
+                " it cannot implement SomeInputObject.",
+            }
+        ]
+
+    def rejects_an_interface_missing_an_interface_argument():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(input: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field argument ParentInterface.field(input:)"
+                " expected but ChildInterface.field does not provide it.",
+                "locations": [(7, 21), (11, 15)],
+            }
+        ]
+
+    def rejects_an_interface_with_an_incorrectly_typed_interface_argument():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(input: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field(input: Int): String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field argument ParentInterface.field(input:)"
+                " expects type String but ChildInterface.field(input:) is type Int.",
+                "locations": [(7, 28), (11, 28)],
+            }
+        ]
+
+    def rejects_an_interface_with_both_an_incorrectly_typed_field_and_argument():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(input: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field(input: Int): Int
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field ParentInterface.field expects type String"
+                " but ChildInterface.field is type Int.",
+                "locations": [(7, 37), (11, 34)],
+            },
+            {
+                "message": "Interface field argument ParentInterface.field(input:)"
+                " expects type String but ChildInterface.field(input:) is type Int.",
+                "locations": [(7, 28), (11, 28)],
+            },
+        ]
+
+    def rejects_an_interface_implementing_an_interface_field_with_additional_args():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field(baseArg: String): String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field(
+                baseArg: String,
+                requiredArg: String!
+                optionalArg1: String,
+                optionalArg2: String = "",
+              ): String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Object field ChildInterface.field includes"
+                " required argument requiredArg that is missing"
+                " from the Interface field ParentInterface.field.",
+                "locations": [(13, 17), (7, 15)],
+            }
+        ]
+
+    def accepts_an_interface_with_an_equivalently_wrapped_interface_field_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field: [String]!
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: [String]!
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_interface_with_a_non_list_interface_field_list_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field: [String]
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field ParentInterface.field"
+                " expects type [String] but ChildInterface.field is type String.",
+                "locations": [(7, 22), (11, 22)],
+            }
+        ]
+
+    def rejects_an_interface_with_a_list_interface_field_non_list_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field: String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: [String]
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field ParentInterface.field expects type String"
+                " but ChildInterface.field is type [String].",
+                "locations": [(7, 22), (11, 22)],
+            }
+        ]
+
+    def accepts_an_interface_with_a_subset_non_null_interface_field_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field: String
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: String!
+            }
+            """
+        )
+        assert validate_schema(schema) == []
+
+    def rejects_an_interface_with_a_superset_nullable_interface_field_type():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface ParentInterface {
+              field: String!
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Interface field ParentInterface.field expects type String!"
+                " but ChildInterface.field is type String.",
+                "locations": [(7, 22), (11, 22)],
+            }
+        ]
+
+    def rejects_an_object_missing_a_transitive_interface():
+        schema = build_schema(
+            """
+            type Query {
+              test: ChildInterface
+            }
+
+            interface SuperInterface {
+              field: String!
+            }
+
+            interface ParentInterface implements SuperInterface {
+              field: String!
+            }
+
+            interface ChildInterface implements ParentInterface {
+              field: String!
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Type ChildInterface must implement SuperInterface"
+                " because it is implemented by ParentInterface.",
+                "locations": [(10, 50), (14, 49)],
+            }
+        ]
+
+    def rejects_a_self_reference_interface():
+        schema = build_schema(
+            """
+            type Query {
+            test: FooInterface
+            }
+
+            interface FooInterface implements FooInterface {
+            field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Type FooInterface cannot implement itself"
+                " because it would create a circular reference.",
+                "locations": [(6, 47)],
+            }
+        ]
+
+    def rejects_a_circular_interface_implementation():
+        schema = build_schema(
+            """
+            type Query {
+              test: FooInterface
+            }
+
+            interface FooInterface implements BarInterface {
+              field: String
+            }
+
+            interface BarInterface implements FooInterface {
+              field: String
+            }
+            """
+        )
+        assert validate_schema(schema) == [
+            {
+                "message": "Type FooInterface cannot implement BarInterface"
+                " because it would create a circular reference.",
+                "locations": [(10, 47), (6, 47)],
+            },
+            {
+                "message": "Type BarInterface cannot implement FooInterface"
+                " because it would create a circular reference.",
+                "locations": [(6, 47), (10, 47)],
+            },
+        ]
+
+
+def describe_assert_valid_schema():
+    def do_not_throw_on_valid_schemas():
+        schema = build_schema(
+            (
+                """
+             type Query {
+               foo: String
+             }
+            """
+            )
+        )
+        assert_valid_schema(schema)
+
+    def include_multiple_errors_into_a_description():
+        schema = build_schema("type SomeType")
+        with raises(TypeError) as exc_info:
+            assert_valid_schema(schema)
+        assert (
+            str(exc_info.value)
+            == dedent(
+                """
+            Query root type must be provided.
+
+            Type SomeType must define one or more fields.
+            """
+            ).rstrip()
+        )
diff --git a/tests/utilities/__init__.py b/tests/utilities/__init__.py
new file mode 100644
index 0000000..911ef26
--- /dev/null
+++ b/tests/utilities/__init__.py
@@ -0,0 +1 @@
+"""Tests for graphql.utilities"""
diff --git a/tests/utilities/test_assert_valid_name.py b/tests/utilities/test_assert_valid_name.py
new file mode 100644
index 0000000..901116d
--- /dev/null
+++ b/tests/utilities/test_assert_valid_name.py
@@ -0,0 +1,29 @@
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.utilities import assert_valid_name
+
+
+def describe_assert_valid_name():
+    def pass_through_valid_name():
+        assert assert_valid_name("_ValidName123") == "_ValidName123"
+
+    def throws_for_use_of_leading_double_underscore():
+        with raises(GraphQLError) as exc_info:
+            assert assert_valid_name("__bad")
+        msg = exc_info.value.message
+        assert msg == (
+            "Name '__bad' must not begin with '__',"
+            " which is reserved by GraphQL introspection."
+        )
+
+    def throws_for_non_strings():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            assert_valid_name({})  # type: ignore
+        msg = str(exc_info.value)
+        assert msg == "Expected name to be a string."
+
+    def throws_for_names_with_invalid_characters():
+        with raises(GraphQLError, match="Names must match"):
+            assert_valid_name(">--()-->")
diff --git a/tests/utilities/test_ast_from_value.py b/tests/utilities/test_ast_from_value.py
new file mode 100644
index 0000000..0fcfbb0
--- /dev/null
+++ b/tests/utilities/test_ast_from_value.py
@@ -0,0 +1,244 @@
+from math import inf, nan
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.language import (
+    BooleanValueNode,
+    EnumValueNode,
+    FloatValueNode,
+    IntValueNode,
+    ListValueNode,
+    NameNode,
+    NullValueNode,
+    ObjectFieldNode,
+    ObjectValueNode,
+    StringValueNode,
+)
+from graphql.pyutils import Undefined
+from graphql.type import (
+    GraphQLBoolean,
+    GraphQLEnumType,
+    GraphQLFloat,
+    GraphQLID,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLScalarType,
+    GraphQLString,
+)
+from graphql.utilities import ast_from_value
+
+
+def describe_ast_from_value():
+    def converts_boolean_values_to_asts():
+        assert ast_from_value(True, GraphQLBoolean) == BooleanValueNode(value=True)
+
+        assert ast_from_value(False, GraphQLBoolean) == BooleanValueNode(value=False)
+
+        assert ast_from_value(Undefined, GraphQLBoolean) is None
+
+        assert ast_from_value(None, GraphQLBoolean) == NullValueNode()
+
+        assert ast_from_value(0, GraphQLBoolean) == BooleanValueNode(value=False)
+
+        assert ast_from_value(1, GraphQLBoolean) == BooleanValueNode(value=True)
+
+        non_null_boolean = GraphQLNonNull(GraphQLBoolean)
+        assert ast_from_value(0, non_null_boolean) == BooleanValueNode(value=False)
+
+    def converts_int_values_to_int_asts():
+        assert ast_from_value(-1, GraphQLInt) == IntValueNode(value="-1")
+
+        assert ast_from_value(123.0, GraphQLInt) == IntValueNode(value="123")
+
+        assert ast_from_value(1e4, GraphQLInt) == IntValueNode(value="10000")
+
+        # GraphQL spec does not allow coercing non-integer values to Int to
+        # avoid accidental data loss.
+        with raises(GraphQLError) as exc_info:
+            assert ast_from_value(123.5, GraphQLInt)
+        msg = str(exc_info.value)
+        assert msg == "Int cannot represent non-integer value: 123.5"
+
+        # Note: outside the bounds of 32bit signed int.
+        with raises(GraphQLError) as exc_info:
+            assert ast_from_value(1e40, GraphQLInt)
+        msg = str(exc_info.value)
+        assert msg == "Int cannot represent non 32-bit signed integer value: 1e+40"
+
+        with raises(GraphQLError) as exc_info:
+            ast_from_value(nan, GraphQLInt)
+        msg = str(exc_info.value)
+        assert msg == "Int cannot represent non-integer value: nan"
+
+    def converts_float_values_to_float_asts():
+        # luckily in Python we can discern between float and int
+        assert ast_from_value(-1, GraphQLFloat) == FloatValueNode(value="-1")
+
+        assert ast_from_value(123.0, GraphQLFloat) == FloatValueNode(value="123")
+
+        assert ast_from_value(123.5, GraphQLFloat) == FloatValueNode(value="123.5")
+
+        assert ast_from_value(1e4, GraphQLFloat) == FloatValueNode(value="10000")
+
+        assert ast_from_value(1e40, GraphQLFloat) == FloatValueNode(value="1e+40")
+
+    def converts_string_values_to_string_asts():
+        assert ast_from_value("hello", GraphQLString) == StringValueNode(value="hello")
+
+        assert ast_from_value("VALUE", GraphQLString) == StringValueNode(value="VALUE")
+
+        assert ast_from_value("VA\nLUE", GraphQLString) == StringValueNode(
+            value="VA\nLUE"
+        )
+
+        assert ast_from_value(123, GraphQLString) == StringValueNode(value="123")
+
+        assert ast_from_value(False, GraphQLString) == StringValueNode(value="false")
+
+        assert ast_from_value(None, GraphQLString) == NullValueNode()
+
+        assert ast_from_value(Undefined, GraphQLString) is None
+
+    def converts_id_values_to_int_or_string_asts():
+        assert ast_from_value("hello", GraphQLID) == StringValueNode(value="hello")
+
+        assert ast_from_value("VALUE", GraphQLID) == StringValueNode(value="VALUE")
+
+        # Note: EnumValues cannot contain non-identifier characters
+        assert ast_from_value("VA\nLUE", GraphQLID) == StringValueNode(value="VA\nLUE")
+
+        # Note: IntValues are used when possible.
+        assert ast_from_value(-1, GraphQLID) == IntValueNode(value="-1")
+
+        assert ast_from_value(123, GraphQLID) == IntValueNode(value="123")
+
+        assert ast_from_value("123", GraphQLID) == IntValueNode(value="123")
+
+        assert ast_from_value("01", GraphQLID) == StringValueNode(value="01")
+
+        with raises(GraphQLError) as exc_info:
+            assert ast_from_value(False, GraphQLID)
+        assert str(exc_info.value) == "ID cannot represent value: False"
+
+        assert ast_from_value(None, GraphQLID) == NullValueNode()
+
+        assert ast_from_value(Undefined, GraphQLString) is None
+
+    def converts_using_serialize_from_a_custom_scalar_type():
+        pass_through_scalar = GraphQLScalarType(
+            "PassThroughScalar", serialize=lambda value: value,
+        )
+
+        assert ast_from_value("value", pass_through_scalar) == StringValueNode(
+            value="value"
+        )
+
+        with raises(TypeError) as exc_info:
+            assert ast_from_value(nan, pass_through_scalar)
+        assert str(exc_info.value) == "Cannot convert value to AST: nan."
+
+        with raises(TypeError) as exc_info:
+            ast_from_value(inf, pass_through_scalar)
+        assert str(exc_info.value) == "Cannot convert value to AST: inf."
+
+        return_null_scalar = GraphQLScalarType(
+            "ReturnNullScalar", serialize=lambda value: None,
+        )
+
+        assert ast_from_value("value", return_null_scalar) is None
+
+        class SomeClass:
+            pass
+
+        return_custom_class_scalar = GraphQLScalarType(
+            "ReturnCustomClassScalar", serialize=lambda value: SomeClass(),
+        )
+
+        with raises(TypeError) as exc_info:
+            ast_from_value("value", return_custom_class_scalar)
+        msg = str(exc_info.value)
+        assert msg == "Cannot convert value to AST: <SomeClass instance>."
+
+    def does_not_convert_non_null_values_to_null_value():
+        non_null_boolean = GraphQLNonNull(GraphQLBoolean)
+        assert ast_from_value(None, non_null_boolean) is None
+
+    complex_value = {"someArbitrary": "complexValue"}
+
+    my_enum = GraphQLEnumType(
+        "MyEnum", {"HELLO": None, "GOODBYE": None, "COMPLEX": complex_value}
+    )
+
+    def converts_string_values_to_enum_asts_if_possible():
+        assert ast_from_value("HELLO", my_enum) == EnumValueNode(value="HELLO")
+
+        assert ast_from_value(complex_value, my_enum) == EnumValueNode(value="COMPLEX")
+
+        # Note: case sensitive
+        with raises(GraphQLError) as exc_info:
+            ast_from_value("hello", my_enum)
+        assert exc_info.value.message == "Enum 'MyEnum' cannot represent value: 'hello'"
+
+        # Note: not a valid enum value
+        with raises(GraphQLError) as exc_info:
+            ast_from_value("UNKNOWN_VALUE", my_enum)
+        assert (
+            exc_info.value.message
+            == "Enum 'MyEnum' cannot represent value: 'UNKNOWN_VALUE'"
+        )
+
+    def converts_list_values_to_list_asts():
+        assert ast_from_value(
+            ["FOO", "BAR"], GraphQLList(GraphQLString)
+        ) == ListValueNode(
+            values=[StringValueNode(value="FOO"), StringValueNode(value="BAR")]
+        )
+
+        assert ast_from_value(
+            ["HELLO", "GOODBYE"], GraphQLList(my_enum)
+        ) == ListValueNode(
+            values=[EnumValueNode(value="HELLO"), EnumValueNode(value="GOODBYE")]
+        )
+
+    def converts_list_singletons():
+        assert ast_from_value("FOO", GraphQLList(GraphQLString)) == StringValueNode(
+            value="FOO"
+        )
+
+    def skips_invalid_list_items():
+        ast = ast_from_value(
+            ["FOO", None, "BAR"], GraphQLList(GraphQLNonNull(GraphQLString))
+        )
+
+        assert ast == ListValueNode(
+            values=[StringValueNode(value="FOO"), StringValueNode(value="BAR")]
+        )
+
+    input_obj = GraphQLInputObjectType(
+        "MyInputObj",
+        {"foo": GraphQLInputField(GraphQLFloat), "bar": GraphQLInputField(my_enum)},
+    )
+
+    def converts_input_objects():
+        assert ast_from_value({"foo": 3, "bar": "HELLO"}, input_obj) == ObjectValueNode(
+            fields=[
+                ObjectFieldNode(
+                    name=NameNode(value="foo"), value=FloatValueNode(value="3")
+                ),
+                ObjectFieldNode(
+                    name=NameNode(value="bar"), value=EnumValueNode(value="HELLO")
+                ),
+            ]
+        )
+
+    def converts_input_objects_with_explicit_nulls():
+        assert ast_from_value({"foo": None}, input_obj) == ObjectValueNode(
+            fields=[ObjectFieldNode(name=NameNode(value="foo"), value=NullValueNode())]
+        )
+
+    def does_not_convert_non_object_values_as_input_objects():
+        assert ast_from_value(5, input_obj) is None
diff --git a/tests/utilities/test_build_ast_schema.py b/tests/utilities/test_build_ast_schema.py
new file mode 100644
index 0000000..1bdc62a
--- /dev/null
+++ b/tests/utilities/test_build_ast_schema.py
@@ -0,0 +1,1239 @@
+from collections import namedtuple
+from typing import Union
+
+from pytest import raises  # type: ignore
+
+from graphql import graphql_sync
+from graphql.language import parse, print_ast, DocumentNode, InterfaceTypeDefinitionNode
+from graphql.type import (
+    GraphQLDeprecatedDirective,
+    GraphQLIncludeDirective,
+    GraphQLSkipDirective,
+    GraphQLSpecifiedByDirective,
+    GraphQLBoolean,
+    GraphQLFloat,
+    GraphQLID,
+    GraphQLInt,
+    GraphQLString,
+    GraphQLArgument,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLNamedType,
+    assert_directive,
+    assert_enum_type,
+    assert_input_object_type,
+    assert_interface_type,
+    assert_object_type,
+    assert_scalar_type,
+    assert_union_type,
+    validate_schema,
+)
+from graphql.utilities import build_ast_schema, build_schema, print_schema, print_type
+
+from ..utils import dedent
+
+
+def cycle_sdl(sdl: str) -> str:
+    """Full cycle test.
+
+    This function does a full cycle of going from a string with the contents of the SDL,
+    parsed in a schema AST, materializing that schema AST into an in-memory
+    GraphQLSchema, and then finally printing that GraphQL into the SDÖ.
+    """
+    ast = parse(sdl)
+    schema = build_ast_schema(ast)
+    return print_schema(schema)
+
+
+TypeWithAstNode = Union[
+    GraphQLArgument, GraphQLEnumValue, GraphQLField, GraphQLInputField, GraphQLNamedType
+]
+
+TypeWithExtensionAstNodes = GraphQLNamedType
+
+
+def print_ast_node(obj: TypeWithAstNode) -> str:
+    assert obj is not None and obj.ast_node is not None
+    return print_ast(obj.ast_node)
+
+
+def print_all_ast_nodes(obj: TypeWithExtensionAstNodes) -> str:
+    assert obj is not None and obj.extension_ast_nodes is not None
+    return print_ast(DocumentNode(definitions=[obj.ast_node, *obj.extension_ast_nodes]))
+
+
+def describe_schema_builder():
+    def can_use_built_schema_for_limited_execution():
+        schema = build_ast_schema(
+            parse(
+                """
+                type Query {
+                  str: String
+                }
+                """
+            )
+        )
+
+        root_value = namedtuple("Data", "str")(123)  # type: ignore
+
+        result = graphql_sync(schema=schema, source="{ str }", root_value=root_value)
+        assert result == ({"str": "123"}, None)
+
+    def can_build_a_schema_directly_from_the_source():
+        schema = build_schema(
+            """
+            type Query {
+              add(x: Int, y: Int): Int
+            }
+            """
+        )
+        source = "{ add(x: 34, y: 55) }"
+
+        # noinspection PyMethodMayBeStatic
+        class RootValue:
+            def add(self, _info, x, y):
+                return x + y
+
+        assert graphql_sync(schema=schema, source=source, root_value=RootValue()) == (
+            {"add": 89},
+            None,
+        )
+
+    def ignores_non_type_system_definitions():
+        sdl = """
+            type Query {
+              str: String
+            }
+
+            fragment SomeFragment on Query {
+              str
+            }
+            """
+        build_schema(sdl)
+
+    def empty_type():
+        sdl = dedent(
+            """
+            type EmptyType
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_type():
+        sdl = dedent(
+            """
+            type Query {
+              str: String
+              int: Int
+              float: Float
+              id: ID
+              bool: Boolean
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+        schema = build_schema(sdl)
+        # Built-ins are used
+        assert schema.get_type("Int") is GraphQLInt
+        assert schema.get_type("Float") is GraphQLFloat
+        assert schema.get_type("String") is GraphQLString
+        assert schema.get_type("Boolean") is GraphQLBoolean
+        assert schema.get_type("ID") is GraphQLID
+
+    def include_standard_type_only_if_it_is_used():
+        schema = build_schema("type Query")
+
+        # Only String and Boolean are used by introspection types
+        assert schema.get_type("Int") is None
+        assert schema.get_type("Float") is None
+        assert schema.get_type("String") is GraphQLString
+        assert schema.get_type("Boolean") is GraphQLBoolean
+        assert schema.get_type("ID") is None
+
+    def with_directives():
+        sdl = dedent(
+            """
+            directive @foo(arg: Int) on FIELD
+
+            directive @repeatableFoo(arg: Int) repeatable on FIELD
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def supports_descriptions():
+        sdl = dedent(
+            '''
+            """Do you agree that this is the most creative schema ever?"""
+            schema {
+              query: Query
+            }
+
+            """This is a directive"""
+            directive @foo(
+              """It has an argument"""
+              arg: Int
+            ) on FIELD
+
+            """With an enum"""
+            enum Color {
+              RED
+
+              """Not a creative color"""
+              GREEN
+              BLUE
+            }
+
+            """What a great type"""
+            type Query {
+              """And a field to boot"""
+              str: String
+            }
+            '''
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def maintains_include_skip_and_specified_by_url_directives():
+        schema = build_schema("type Query")
+
+        assert len(schema.directives) == 4
+        assert schema.get_directive("skip") is GraphQLSkipDirective
+        assert schema.get_directive("include") is GraphQLIncludeDirective
+        assert schema.get_directive("deprecated") is GraphQLDeprecatedDirective
+        assert schema.get_directive("specifiedBy") is GraphQLSpecifiedByDirective
+
+    def overriding_directives_excludes_specified():
+        schema = build_schema(
+            """
+            directive @skip on FIELD
+            directive @include on FIELD
+            directive @deprecated on FIELD_DEFINITION
+            directive @specifiedBy on FIELD_DEFINITION
+            """
+        )
+
+        assert len(schema.directives) == 4
+        get_directive = schema.get_directive
+        assert get_directive("skip") is not GraphQLSkipDirective
+        assert get_directive("skip") is not None
+        assert get_directive("include") is not GraphQLIncludeDirective
+        assert get_directive("include") is not None
+        assert get_directive("deprecated") is not GraphQLDeprecatedDirective
+        assert get_directive("deprecated") is not None
+        assert get_directive("specifiedBy") is not GraphQLSpecifiedByDirective
+        assert get_directive("specifiedBy") is not None
+
+    def adding_directives_maintains_include_skip_and_specified_by_directives():
+        schema = build_schema(
+            """
+            directive @foo(arg: Int) on FIELD
+            """
+        )
+
+        assert len(schema.directives) == 5
+        assert schema.get_directive("skip") is GraphQLSkipDirective
+        assert schema.get_directive("include") is GraphQLIncludeDirective
+        assert schema.get_directive("deprecated") is GraphQLDeprecatedDirective
+        assert schema.get_directive("specifiedBy") is GraphQLSpecifiedByDirective
+        assert schema.get_directive("foo") is not None
+
+    def type_modifiers():
+        sdl = dedent(
+            """
+            type Query {
+              nonNullStr: String!
+              listOfStrings: [String]
+              listOfNonNullStrings: [String!]
+              nonNullListOfStrings: [String]!
+              nonNullListOfNonNullStrings: [String!]!
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def recursive_type():
+        sdl = dedent(
+            """
+            type Query {
+              str: String
+              recurse: Query
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def two_types_circular():
+        sdl = dedent(
+            """
+            type TypeOne {
+              str: String
+              typeTwo: TypeTwo
+            }
+
+            type TypeTwo {
+              str: String
+              typeOne: TypeOne
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def single_argument_field():
+        sdl = dedent(
+            """
+            type Query {
+              str(int: Int): String
+              floatToStr(float: Float): String
+              idToStr(id: ID): String
+              booleanToStr(bool: Boolean): String
+              strToStr(bool: String): String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_type_with_multiple_arguments():
+        sdl = dedent(
+            """
+            type Query {
+              str(int: Int, bool: Boolean): String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def empty_interface():
+        sdl = dedent(
+            """
+            interface EmptyInterface
+            """
+        )
+
+        definition = parse(sdl).definitions[0]
+        assert isinstance(definition, InterfaceTypeDefinitionNode)
+        assert definition.interfaces == []
+
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_type_with_interface():
+        sdl = dedent(
+            """
+            type Query implements WorldInterface {
+              str: String
+            }
+
+            interface WorldInterface {
+              str: String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_interface_hierarchy():
+        sdl = dedent(
+            """
+            schema {
+              query: Child
+            }
+
+            interface Child implements Parent {
+              str: String
+            }
+
+            type Hello implements Parent & Child {
+              str: String
+            }
+
+            interface Parent {
+              str: String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def empty_enum():
+        sdl = dedent(
+            """
+            enum EmptyEnum
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_output_enum():
+        sdl = dedent(
+            """
+            enum Hello {
+              WORLD
+            }
+
+            type Query {
+              hello: Hello
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_input_enum():
+        sdl = dedent(
+            """
+            enum Hello {
+              WORLD
+            }
+
+            type Query {
+              str(hello: Hello): String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def multiple_value_enum():
+        sdl = dedent(
+            """
+            enum Hello {
+              WO
+              RLD
+            }
+
+            type Query {
+              hello: Hello
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def empty_union():
+        sdl = dedent(
+            """
+            union EmptyUnion
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_union():
+        sdl = dedent(
+            """
+            union Hello = World
+
+            type Query {
+              hello: Hello
+            }
+
+            type World {
+              str: String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def multiple_union():
+        sdl = dedent(
+            """
+            union Hello = WorldOne | WorldTwo
+
+            type Query {
+              hello: Hello
+            }
+
+            type WorldOne {
+              str: String
+            }
+
+            type WorldTwo {
+              str: String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def can_build_recursive_union():
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            build_schema(
+                """
+                union Hello = Hello
+
+                type Query {
+                  hello: Hello
+                }
+                """
+            )
+        assert (
+            str(exc_info.value) == "Hello types must be specified"
+            " as a collection of GraphQLObjectType instances."
+        )
+
+    def describe_specifying_union_type_using_typename():
+
+        schema = build_schema(
+            """
+            type Query {
+              fruits: [Fruit]
+            }
+
+            union Fruit = Apple | Banana
+
+            type Apple {
+              color: String
+            }
+
+            type Banana {
+              length: Int
+            }
+            """
+        )
+
+        source = """
+            {
+              fruits {
+                ... on Apple {
+                  color
+                }
+                ... on Banana {
+                  length
+                }
+              }
+            }
+            """
+
+        expected = ({"fruits": [{"color": "green"}, {"length": 5}]}, None)
+
+        def using_dicts():
+            root_value = {
+                "fruits": [
+                    {"color": "green", "__typename": "Apple"},
+                    {"length": 5, "__typename": "Banana"},
+                ]
+            }
+
+            assert (
+                graphql_sync(schema=schema, source=source, root_value=root_value)
+                == expected
+            )
+
+        def using_objects():
+            class Apple:
+                __typename = "Apple"
+                color = "green"
+
+            class Banana:
+                __typename = "Banana"
+                length = 5
+
+            class RootValue:
+                fruits = [Apple(), Banana()]
+
+            assert (
+                graphql_sync(schema=schema, source=source, root_value=RootValue())
+                == expected
+            )
+
+        def using_inheritance():
+            class Fruit:
+                __typename = "Fruit"
+
+            class Apple(Fruit):
+                __typename = "Apple"
+
+            class Delicious(Apple):
+                color = "golden or red"
+
+            class GoldenDelicious(Delicious):
+                color = "golden"
+
+            class RedDelicious(Delicious):
+                color = "red"
+
+            class GrannySmith(Apple):
+                color = "green"
+
+            class Banana(Fruit):
+                __typename = "Banana"
+                length = 5
+
+            class RootValue:
+                fruits = [GrannySmith(), RedDelicious(), GoldenDelicious(), Banana()]
+
+            assert graphql_sync(
+                schema=schema, source=source, root_value=RootValue()
+            ) == (
+                {
+                    "fruits": [
+                        {"color": "green"},
+                        {"color": "red"},
+                        {"color": "golden"},
+                        {"length": 5},
+                    ]
+                },
+                None,
+            )
+
+    def describe_specifying_interface_type_using_typename():
+        schema = build_schema(
+            """
+            type Query {
+              characters: [Character]
+            }
+
+            interface Character {
+              name: String!
+            }
+
+            type Human implements Character {
+              name: String!
+              totalCredits: Int
+            }
+
+            type Droid implements Character {
+              name: String!
+              primaryFunction: String
+            }
+            """
+        )
+
+        source = """
+            {
+              characters {
+                name
+                ... on Human {
+                  totalCredits
+                }
+                ... on Droid {
+                  primaryFunction
+                }
+              }
+            }
+            """
+
+        expected = (
+            {
+                "characters": [
+                    {"name": "Han Solo", "totalCredits": 10},
+                    {"name": "R2-D2", "primaryFunction": "Astromech"},
+                ]
+            },
+            None,
+        )
+
+        def using_dicts():
+            root_value = {
+                "characters": [
+                    {"name": "Han Solo", "totalCredits": 10, "__typename": "Human"},
+                    {
+                        "name": "R2-D2",
+                        "primaryFunction": "Astromech",
+                        "__typename": "Droid",
+                    },
+                ]
+            }
+
+            assert (
+                graphql_sync(schema=schema, source=source, root_value=root_value)
+                == expected
+            )
+
+        def using_objects():
+            class Human:
+                __typename = "Human"
+                name = "Han Solo"
+                totalCredits = 10
+
+            class Droid:
+                __typename = "Droid"
+                name = "R2-D2"
+                primaryFunction = "Astromech"
+
+            class RootValue:
+                characters = [Human(), Droid()]
+
+            assert (
+                graphql_sync(schema=schema, source=source, root_value=RootValue())
+                == expected
+            )
+
+        def using_inheritance():
+            class Character:
+                __typename = "Character"
+
+            class Human(Character):
+                __typename = "Human"
+
+            class HanSolo(Human):
+                name = "Han Solo"
+                totalCredits = 10
+
+            class Droid(Character):
+                __typename = "Droid"
+
+            class RemoteControlled:
+                name = "R2"
+
+            class Mobile:
+                name = "D2"
+
+            class R2D2(RemoteControlled, Droid, Mobile):
+                name = "R2-D2"
+                primaryFunction = "Astromech"
+
+            class RootValue:
+                characters = [HanSolo(), R2D2()]
+
+            assert (
+                graphql_sync(schema=schema, source=source, root_value=RootValue())
+                == expected
+            )
+
+    def custom_scalar():
+        sdl = dedent(
+            """
+            scalar CustomScalar
+
+            type Query {
+              customScalar: CustomScalar
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def empty_input_object():
+        sdl = dedent(
+            """
+            input EmptyInputObject
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_input_object():
+        sdl = dedent(
+            """
+            input Input {
+              int: Int
+            }
+
+            type Query {
+              field(in: Input): String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_argument_field_with_default():
+        sdl = dedent(
+            """
+            type Query {
+              str(int: Int = 2): String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def custom_scalar_argument_field_with_default():
+        sdl = dedent(
+            """
+            scalar CustomScalar
+
+            type Query {
+              str(int: CustomScalar = 2): String
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_type_with_mutation():
+        sdl = dedent(
+            """
+            schema {
+              query: HelloScalars
+              mutation: Mutation
+            }
+
+            type HelloScalars {
+              str: String
+              int: Int
+              bool: Boolean
+            }
+
+            type Mutation {
+              addHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def simple_type_with_subscription():
+        sdl = dedent(
+            """
+            schema {
+              query: HelloScalars
+              subscription: Subscription
+            }
+
+            type HelloScalars {
+              str: String
+              int: Int
+              bool: Boolean
+            }
+
+            type Subscription {
+              subscribeHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def unreferenced_type_implementing_referenced_interface():
+        sdl = dedent(
+            """
+            type Concrete implements Interface {
+              key: String
+            }
+
+            interface Interface {
+              key: String
+            }
+
+            type Query {
+              interface: Interface
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def unreferenced_interface_implementing_referenced_interface():
+        sdl = dedent(
+            """
+            interface Child implements Parent {
+              key: String
+            }
+
+            interface Parent {
+              key: String
+            }
+
+            type Query {
+              interfaceField: Parent
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def unreferenced_type_implementing_referenced_union():
+        sdl = dedent(
+            """
+            type Concrete {
+              key: String
+            }
+
+            type Query {
+              union: Union
+            }
+
+            union Union = Concrete
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+    def supports_deprecated_directive():
+        sdl = dedent(
+            """
+            enum MyEnum {
+              VALUE
+              OLD_VALUE @deprecated
+              OTHER_VALUE @deprecated(reason: "Terrible reasons")
+            }
+
+            type Query {
+              field1: String @deprecated
+              field2: Int @deprecated(reason: "Because I said so")
+              enum: MyEnum
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+        schema = build_schema(sdl)
+
+        my_enum = assert_enum_type(schema.get_type("MyEnum"))
+
+        value = my_enum.values["VALUE"]
+        assert value.is_deprecated is False
+
+        old_value = my_enum.values["OLD_VALUE"]
+        assert old_value.is_deprecated is True
+        assert old_value.deprecation_reason == "No longer supported"
+
+        other_value = my_enum.values["OTHER_VALUE"]
+        assert other_value.is_deprecated is True
+        assert other_value.deprecation_reason == "Terrible reasons"
+
+        root_fields = assert_object_type(schema.get_type("Query")).fields
+        field1 = root_fields["field1"]
+        assert field1.is_deprecated is True
+        assert field1.deprecation_reason == "No longer supported"
+        field2 = root_fields["field2"]
+        assert field2.is_deprecated is True
+        assert field2.deprecation_reason == "Because I said so"
+
+    def supports_specified_by_directives():
+        sdl = dedent(
+            """
+            scalar Foo @specifiedBy(url: "https://example.com/foo_spec")
+
+            type Query {
+              foo: Foo @deprecated
+            }
+            """
+        )
+        assert cycle_sdl(sdl) == sdl
+
+        schema = build_schema(sdl)
+
+        foo_scalar = assert_scalar_type(schema.get_type("Foo"))
+        assert foo_scalar.specified_by_url == "https://example.com/foo_spec"
+
+    def correctly_extend_scalar_type():
+        scalar_sdl = dedent(
+            """
+            scalar SomeScalar
+
+            extend scalar SomeScalar @foo
+
+            extend scalar SomeScalar @bar
+            """
+        )
+        schema = build_schema(
+            scalar_sdl
+            + dedent(
+                """
+                directive @foo on SCALAR
+                directive @bar on SCALAR
+                """
+            )
+        )
+
+        some_scalar = assert_scalar_type(schema.get_type("SomeScalar"))
+        assert print_type(some_scalar) + "\n" == dedent(
+            """
+            scalar SomeScalar
+            """
+        )
+
+        assert print_all_ast_nodes(some_scalar) == scalar_sdl
+
+    def correctly_extend_object_type():
+        object_sdl = dedent(
+            """
+            type SomeObject implements Foo {
+              first: String
+            }
+
+            extend type SomeObject implements Bar {
+              second: Int
+            }
+
+            extend type SomeObject implements Baz {
+              third: Float
+            }
+            """
+        )
+        schema = build_schema(
+            object_sdl
+            + dedent(
+                """
+                interface Foo
+                interface Bar
+                interface Baz
+                """
+            )
+        )
+
+        some_object = assert_object_type(schema.get_type("SomeObject"))
+        assert print_type(some_object) + "\n" == dedent(
+            """
+            type SomeObject implements Foo & Bar & Baz {
+              first: String
+              second: Int
+              third: Float
+            }
+            """
+        )
+
+        assert print_all_ast_nodes(some_object) == object_sdl
+
+    def correctly_extend_interface_type():
+        interface_sdl = dedent(
+            """
+            interface SomeInterface {
+              first: String
+            }
+
+            extend interface SomeInterface {
+              second: Int
+            }
+
+            extend interface SomeInterface {
+              third: Float
+            }
+            """
+        )
+        schema = build_schema(interface_sdl)
+
+        some_interface = assert_interface_type(schema.get_type("SomeInterface"))
+        assert print_type(some_interface) + "\n" == dedent(
+            """
+            interface SomeInterface {
+              first: String
+              second: Int
+              third: Float
+            }
+            """
+        )
+
+        assert print_all_ast_nodes(some_interface) == interface_sdl
+
+    def correctly_extend_union_type():
+        union_sdl = dedent(
+            """
+            union SomeUnion = FirstType
+
+            extend union SomeUnion = SecondType
+
+            extend union SomeUnion = ThirdType
+            """
+        )
+        schema = build_schema(
+            union_sdl
+            + dedent(
+                """
+                type FirstType
+                type SecondType
+                type ThirdType
+                """
+            )
+        )
+
+        some_union = assert_union_type(schema.get_type("SomeUnion"))
+        assert print_type(some_union) + "\n" == dedent(
+            """
+            union SomeUnion = FirstType | SecondType | ThirdType
+            """
+        )
+
+        assert print_all_ast_nodes(some_union) == union_sdl
+
+    def correctly_extend_enum_type():
+        enum_sdl = dedent(
+            """
+            enum SomeEnum {
+              FIRST
+            }
+
+            extend enum SomeEnum {
+              SECOND
+            }
+
+            extend enum SomeEnum {
+              THIRD
+            }
+            """
+        )
+        schema = build_schema(enum_sdl)
+
+        some_enum = assert_enum_type(schema.get_type("SomeEnum"))
+        assert print_type(some_enum) + "\n" == dedent(
+            """
+            enum SomeEnum {
+              FIRST
+              SECOND
+              THIRD
+            }
+            """
+        )
+
+        assert print_all_ast_nodes(some_enum) == enum_sdl
+
+    def correctly_extend_input_object_type():
+        input_sdl = dedent(
+            """
+            input SomeInput {
+              first: String
+            }
+
+            extend input SomeInput {
+              second: Int
+            }
+
+            extend input SomeInput {
+              third: Float
+            }
+            """
+        )
+        schema = build_schema(input_sdl)
+
+        some_input = assert_input_object_type(schema.get_type("SomeInput"))
+        assert print_type(some_input) + "\n" == dedent(
+            """
+            input SomeInput {
+              first: String
+              second: Int
+              third: Float
+            }
+            """
+        )
+
+        assert print_all_ast_nodes(some_input) == input_sdl
+
+    def correctly_assign_ast_nodes():
+        sdl = dedent(
+            """
+            schema {
+              query: Query
+            }
+
+            type Query {
+              testField(testArg: TestInput): TestUnion
+            }
+
+            input TestInput {
+              testInputField: TestEnum
+            }
+
+            enum TestEnum {
+              TEST_VALUE
+            }
+
+            union TestUnion = TestType
+
+            interface TestInterface {
+              interfaceField: String
+            }
+
+            type TestType implements TestInterface {
+              interfaceField: String
+            }
+
+            scalar TestScalar
+
+            directive @test(arg: TestScalar) on FIELD
+            """
+        )
+        ast = parse(sdl, no_location=True)
+
+        schema = build_ast_schema(ast)
+        query = assert_object_type(schema.get_type("Query"))
+        test_input = assert_input_object_type(schema.get_type("TestInput"))
+        test_enum = assert_enum_type(schema.get_type("TestEnum"))
+        test_union = assert_union_type(schema.get_type("TestUnion"))
+        test_interface = assert_interface_type(schema.get_type("TestInterface"))
+        test_type = assert_object_type(schema.get_type("TestType"))
+        test_scalar = assert_scalar_type(schema.get_type("TestScalar"))
+        test_directive = assert_directive(schema.get_directive("test"))
+
+        assert [
+            schema.ast_node,
+            query.ast_node,
+            test_input.ast_node,
+            test_enum.ast_node,
+            test_union.ast_node,
+            test_interface.ast_node,
+            test_type.ast_node,
+            test_scalar.ast_node,
+            test_directive.ast_node,
+        ] == ast.definitions
+
+        test_field = query.fields["testField"]
+        assert print_ast_node(test_field) == (
+            "testField(testArg: TestInput): TestUnion"
+        )
+        assert print_ast_node(test_field.args["testArg"]) == "testArg: TestInput"
+        assert print_ast_node(test_input.fields["testInputField"]) == (
+            "testInputField: TestEnum"
+        )
+        test_enum_value = test_enum.values["TEST_VALUE"]
+        assert test_enum_value
+        assert print_ast_node(test_enum_value) == "TEST_VALUE"
+        assert print_ast_node(test_interface.fields["interfaceField"]) == (
+            "interfaceField: String"
+        )
+        assert print_ast_node(test_directive.args["arg"]) == "arg: TestScalar"
+
+    def root_operation_types_with_custom_names():
+        schema = build_schema(
+            """
+            schema {
+              query: SomeQuery
+              mutation: SomeMutation
+              subscription: SomeSubscription
+            }
+            type SomeQuery
+            type SomeMutation
+            type SomeSubscription
+            """
+        )
+
+        assert schema.query_type
+        assert schema.query_type.name == "SomeQuery"
+        assert schema.mutation_type
+        assert schema.mutation_type.name == "SomeMutation"
+        assert schema.subscription_type
+        assert schema.subscription_type.name == "SomeSubscription"
+
+    def default_root_operation_type_names():
+        schema = build_schema(
+            """
+            type Query
+            type Mutation
+            type Subscription
+            """
+        )
+
+        assert schema.query_type
+        assert schema.query_type.name == "Query"
+        assert schema.mutation_type
+        assert schema.mutation_type.name == "Mutation"
+        assert schema.subscription_type
+        assert schema.subscription_type.name == "Subscription"
+
+    def can_build_invalid_schema():
+        # Invalid schema, because it is missing query root type
+        schema = build_schema("type Mutation")
+        errors = validate_schema(schema)
+        assert errors
+
+    def rejects_invalid_sdl():
+        sdl = """
+            type Query {
+              foo: String @unknown
+            }
+            """
+        with raises(TypeError) as exc_info:
+            build_schema(sdl)
+        assert str(exc_info.value) == "Unknown directive '@unknown'."
+
+    def allows_to_disable_sdl_validation():
+        sdl = """
+            type Query {
+              foo: String @unknown
+            }
+            """
+        build_schema(sdl, assume_valid=True)
+        build_schema(sdl, assume_valid_sdl=True)
+
+    def throws_on_unknown_types():
+        sdl = """
+            type Query {
+              unknown: UnknownType
+            }
+            """
+        with raises(TypeError) as exc_info:
+            build_schema(sdl, assume_valid_sdl=True)
+        assert str(exc_info.value).endswith("Unknown type: 'UnknownType'.")
+
+    def rejects_invalid_ast():
+        with raises(TypeError) as exc_info:
+            build_ast_schema(None)  # type: ignore
+        assert str(exc_info.value) == "Must provide valid Document AST."
+        with raises(TypeError) as exc_info:
+            build_ast_schema({})  # type: ignore
+        assert str(exc_info.value) == "Must provide valid Document AST."
diff --git a/tests/utilities/test_build_client_schema.py b/tests/utilities/test_build_client_schema.py
new file mode 100644
index 0000000..0efe16c
--- /dev/null
+++ b/tests/utilities/test_build_client_schema.py
@@ -0,0 +1,1040 @@
+from pytest import raises  # type: ignore
+
+from graphql import graphql_sync
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLEnumType,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLFloat,
+    GraphQLID,
+    GraphQLInt,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLString,
+    assert_enum_type,
+)
+from graphql.utilities import (
+    build_schema,
+    build_client_schema,
+    introspection_from_schema,
+    print_schema,
+)
+
+from ..utils import dedent
+
+
+def cycle_introspection(sdl_string):
+    """Test that the client side introspection gives the same result.
+
+    This function does a full cycle of going from a string with the contents of the SDL,
+    build in-memory GraphQLSchema from it, produce a client-side representation of the
+    schema by using "build_client_schema" and then return that schema printed as SDL.
+    """
+    options = dict(specified_by_url=True, directive_is_repeatable=True)
+
+    server_schema = build_schema(sdl_string)
+    initial_introspection = introspection_from_schema(server_schema, **options)
+    client_schema = build_client_schema(initial_introspection)
+    # If the client then runs the introspection query against the client-side schema,
+    # it should get a result identical to what was returned by the server
+    second_introspection = introspection_from_schema(client_schema, **options)
+
+    # If the client then runs the introspection query against the client-side
+    # schema, it should get a result identical to what was returned by the server.
+    assert initial_introspection == second_introspection
+    return print_schema(client_schema)
+
+
+def describe_type_system_build_schema_from_introspection():
+    def builds_a_simple_schema():
+        sdl = dedent(
+            '''
+            """Simple schema"""
+            schema {
+              query: Simple
+            }
+
+            """This is a simple type"""
+            type Simple {
+              """This is a string field"""
+              string: String
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_without_the_query_type():
+        sdl = dedent(
+            """
+            type Query {
+              foo: String
+            }
+            """
+        )
+
+        schema = build_schema(sdl)
+        introspection = introspection_from_schema(schema)
+        del introspection["__schema"]["queryType"]
+
+        client_schema = build_client_schema(introspection)
+        assert client_schema.query_type is None
+        assert print_schema(client_schema) == sdl
+
+    def builds_a_simple_schema_with_all_operation_types():
+        sdl = dedent(
+            '''
+            schema {
+              query: QueryType
+              mutation: MutationType
+              subscription: SubscriptionType
+            }
+
+            """This is a simple mutation type"""
+            type MutationType {
+              """Set the string field"""
+              string: String
+            }
+
+            """This is a simple query type"""
+            type QueryType {
+              """This is a string field"""
+              string: String
+            }
+
+            """This is a simple subscription type"""
+            type SubscriptionType {
+              """This is a string field"""
+              string: String
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def uses_built_in_scalars_when_possible():
+        sdl = dedent(
+            """
+            scalar CustomScalar
+
+            type Query {
+              int: Int
+              float: Float
+              string: String
+              boolean: Boolean
+              id: ID
+              custom: CustomScalar
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+        schema = build_schema(sdl)
+        introspection = introspection_from_schema(schema)
+        client_schema = build_client_schema(introspection)
+
+        # Built-ins are used
+        assert client_schema.get_type("Int") is GraphQLInt
+        assert client_schema.get_type("Float") is GraphQLFloat
+        assert client_schema.get_type("String") is GraphQLString
+        assert client_schema.get_type("Boolean") is GraphQLBoolean
+        assert client_schema.get_type("ID") is GraphQLID
+
+        # Custom are built
+        custom_scalar = schema.get_type("CustomScalar")
+        assert client_schema.get_type("CustomScalar") is not custom_scalar
+
+    def includes_standard_types_only_if_they_are_used():
+        schema = build_schema(
+            """
+            type Query {
+              foo: String
+            }
+            """
+        )
+        introspection = introspection_from_schema(schema)
+        client_schema = build_client_schema(introspection)
+
+        assert client_schema.get_type("Int") is None
+        assert client_schema.get_type("Float") is None
+        assert client_schema.get_type("ID") is None
+
+    def builds_a_schema_with_a_recursive_type_reference():
+        sdl = dedent(
+            """
+            schema {
+              query: Recur
+            }
+
+            type Recur {
+              recur: Recur
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_a_circular_type_reference():
+        sdl = dedent(
+            """
+            type Dog {
+              bestFriend: Human
+            }
+
+            type Human {
+              bestFriend: Dog
+            }
+
+            type Query {
+              dog: Dog
+              human: Human
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_an_interface():
+        sdl = dedent(
+            '''
+            type Dog implements Friendly {
+              bestFriend: Friendly
+            }
+
+            interface Friendly {
+              """The best friend of this friendly thing"""
+              bestFriend: Friendly
+            }
+
+            type Human implements Friendly {
+              bestFriend: Friendly
+            }
+
+            type Query {
+              friendly: Friendly
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_an_interface_hierarchy():
+        sdl = dedent(
+            '''
+            type Dog implements Friendly & Named {
+              bestFriend: Friendly
+              name: String
+            }
+
+            interface Friendly implements Named {
+              """The best friend of this friendly thing"""
+              bestFriend: Friendly
+              name: String
+            }
+
+            type Human implements Friendly & Named {
+              bestFriend: Friendly
+              name: String
+            }
+
+            interface Named {
+              name: String
+            }
+
+            type Query {
+              friendly: Friendly
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_an_implicit_interface():
+        sdl = dedent(
+            '''
+            type Dog implements Friendly {
+              bestFriend: Friendly
+            }
+
+            interface Friendly {
+              """The best friend of this friendly thing"""
+              bestFriend: Friendly
+            }
+
+            type Query {
+              dog: Dog
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_a_union():
+        sdl = dedent(
+            """
+            type Dog {
+              bestFriend: Friendly
+            }
+
+            union Friendly = Dog | Human
+
+            type Human {
+              bestFriend: Friendly
+            }
+
+            type Query {
+              friendly: Friendly
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_complex_field_values():
+        sdl = dedent(
+            """
+            type Query {
+              string: String
+              listOfString: [String]
+              nonNullString: String!
+              nonNullListOfString: [String]!
+              nonNullListOfNonNullString: [String!]!
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_field_arguments():
+        sdl = dedent(
+            '''
+            type Query {
+              """A field with a single arg"""
+              one(
+                """This is an int arg"""
+                intArg: Int
+              ): String
+
+              """A field with a two args"""
+              two(
+                """This is an list of int arg"""
+                listArg: [Int]
+
+                """This is a required arg"""
+                requiredArg: Boolean!
+              ): String
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_default_value_on_custom_scalar_field():
+        sdl = dedent(
+            """
+            scalar CustomScalar
+
+            type Query {
+              testField(testArg: CustomScalar = "default"): String
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_an_enum():
+        food_enum = GraphQLEnumType(
+            "Food",
+            {
+                "VEGETABLES": GraphQLEnumValue(
+                    1, description="Foods that are vegetables."
+                ),
+                "FRUITS": GraphQLEnumValue(2),
+                "OILS": GraphQLEnumValue(3, deprecation_reason="Too fatty."),
+            },
+            description="Varieties of food stuffs",
+        )
+
+        schema = GraphQLSchema(
+            GraphQLObjectType(
+                "EnumFields",
+                {
+                    "food": GraphQLField(
+                        food_enum,
+                        args={
+                            "kind": GraphQLArgument(
+                                food_enum, description="what kind of food?"
+                            )
+                        },
+                        description="Repeats the arg you give it",
+                    )
+                },
+            )
+        )
+
+        introspection = introspection_from_schema(schema)
+        client_schema = build_client_schema(introspection)
+
+        second_introspection = introspection_from_schema(client_schema)
+        assert second_introspection == introspection
+
+        # It's also an Enum type on the client.
+        client_food_enum = assert_enum_type(client_schema.get_type("Food"))
+
+        # Client types do not get server-only values, so they are set to None
+        # rather than using the integers defined in the "server" schema.
+        values = {
+            name: value.to_kwargs() for name, value in client_food_enum.values.items()
+        }
+        assert values == {
+            "VEGETABLES": {
+                "value": None,
+                "description": "Foods that are vegetables.",
+                "deprecation_reason": None,
+                "extensions": None,
+                "ast_node": None,
+            },
+            "FRUITS": {
+                "value": None,
+                "description": None,
+                "deprecation_reason": None,
+                "extensions": None,
+                "ast_node": None,
+            },
+            "OILS": {
+                "value": None,
+                "description": None,
+                "deprecation_reason": "Too fatty.",
+                "extensions": None,
+                "ast_node": None,
+            },
+        }
+
+    def builds_a_schema_with_an_input_object():
+        sdl = dedent(
+            '''
+            """An input address"""
+            input Address {
+              """What street is this address?"""
+              street: String!
+
+              """The city the address is within?"""
+              city: String!
+
+              """The country (blank will assume USA)."""
+              country: String = "USA"
+            }
+
+            type Query {
+              """Get a geocode from an address"""
+              geocode(
+                """The address to lookup"""
+                address: Address
+              ): String
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_field_arguments_with_default_values():
+        sdl = dedent(
+            """
+            input Geo {
+              lat: Float
+              lon: Float
+            }
+
+            type Query {
+              defaultInt(intArg: Int = 30): String
+              defaultList(listArg: [Int] = [1, 2, 3]): String
+              defaultObject(objArg: Geo = {lat: 37.485, lon: -122.148}): String
+              defaultNull(intArg: Int = null): String
+              noDefault(intArg: Int): String
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_custom_directives():
+        sdl = dedent(
+            '''
+            """This is a custom directive"""
+            directive @customDirective repeatable on FIELD
+
+            type Query {
+              string: String
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_without_directives():
+        sdl = dedent(
+            """
+            type Query {
+              foo: String
+            }
+            """
+        )
+
+        schema = build_schema(sdl)
+        introspection = introspection_from_schema(schema)
+        del introspection["__schema"]["directives"]
+
+        client_schema = build_client_schema(introspection)
+
+        assert schema.directives
+        assert client_schema.directives == []
+        assert print_schema(client_schema) == sdl
+
+    def builds_a_schema_aware_of_deprecation():
+        sdl = dedent(
+            '''
+            enum Color {
+              """So rosy"""
+              RED
+
+              """So grassy"""
+              GREEN
+
+              """So calming"""
+              BLUE
+
+              """So sickening"""
+              MAUVE @deprecated(reason: "No longer in fashion")
+            }
+
+            type Query {
+              """This is a shiny string field"""
+              shinyString: String
+
+              """This is a deprecated string field"""
+              deprecatedString: String @deprecated(reason: "Use shinyString")
+              color: Color
+            }
+            '''
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_empty_deprecation_reasons():
+        sdl = dedent(
+            """
+            type Query {
+              someField: String @deprecated(reason: "")
+            }
+
+            enum SomeEnum {
+              SOME_VALUE @deprecated(reason: "")
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def builds_a_schema_with_specified_by_url():
+        sdl = dedent(
+            """
+            scalar Foo @specifiedBy(url: "https://example.com/foo_spec")
+
+            type Query {
+              foo: Foo
+            }
+            """
+        )
+
+        assert cycle_introspection(sdl) == sdl
+
+    def can_use_client_schema_for_limited_execution():
+        schema = build_schema(
+            """
+            scalar CustomScalar
+
+            type Query {
+              foo(custom1: CustomScalar, custom2: CustomScalar): String
+            }
+            """
+        )
+
+        introspection = introspection_from_schema(schema)
+        client_schema = build_client_schema(introspection)
+
+        class Data:
+            foo = "bar"
+            unused = "value"
+
+        result = graphql_sync(
+            client_schema,
+            "query Limited($v: CustomScalar) { foo(custom1: 123, custom2: $v) }",
+            root_value=Data(),
+            variable_values={"v": "baz"},
+        )
+
+        assert result.data == {"foo": "bar"}
+
+    def can_build_invalid_schema():
+        schema = build_schema("type Query", assume_valid=True)
+
+        introspection = introspection_from_schema(schema)
+        client_schema = build_client_schema(introspection, assume_valid=True)
+
+        assert client_schema.to_kwargs()["assume_valid"] is True
+
+    def describe_throws_when_given_invalid_introspection():
+        dummy_schema = build_schema(
+            """
+            type Query {
+              foo(bar: String): String
+            }
+
+            interface SomeInterface {
+              foo: String
+            }
+
+            union SomeUnion = Query
+
+            enum SomeEnum { FOO }
+
+            input SomeInputObject {
+              foo: String
+            }
+
+            directive @SomeDirective on QUERY
+            """
+        )
+
+        def throws_when_introspection_is_missing_schema_property():
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                build_client_schema(None)  # type: ignore
+
+            assert str(exc_info.value) == (
+                "Invalid or incomplete introspection result. Ensure that you"
+                " are passing the 'data' attribute of an introspection response"
+                " and no 'errors' were returned alongside: None."
+            )
+
+            with raises(TypeError) as exc_info:
+                # noinspection PyTypeChecker
+                build_client_schema({})
+
+            assert str(exc_info.value) == (
+                "Invalid or incomplete introspection result. Ensure that you"
+                " are passing the 'data' attribute of an introspection response"
+                " and no 'errors' were returned alongside: {}."
+            )
+
+        def throws_when_referenced_unknown_type():
+            introspection = introspection_from_schema(dummy_schema)
+
+            introspection["__schema"]["types"] = [
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] != "Query"
+            ]
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+
+            assert str(exc_info.value) == (
+                "Invalid or incomplete schema, unknown type: Query."
+                " Ensure that a full introspection query is used"
+                " in order to build a client schema."
+            )
+
+        def throws_when_missing_definition_for_one_of_the_standard_scalars():
+            schema = build_schema(
+                """
+                type Query {
+                  foo: Float
+                }
+                """
+            )
+            introspection = introspection_from_schema(schema)
+            introspection["__schema"]["types"] = [
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] != "Float"
+            ]
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+
+            assert str(exc_info.value).endswith(
+                "Invalid or incomplete schema, unknown type: Float."
+                " Ensure that a full introspection query is used"
+                " in order to build a client schema."
+            )
+
+        def throws_when_type_reference_is_missing_name():
+            introspection = introspection_from_schema(dummy_schema)
+
+            assert introspection["__schema"]["queryType"]["name"] == "Query"
+            del introspection["__schema"]["queryType"]["name"]
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+
+            assert str(exc_info.value) == "Unknown type reference: {}."
+
+        def throws_when_missing_kind():
+            introspection = introspection_from_schema(dummy_schema)
+
+            query_type_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "Query"
+            )
+            assert query_type_introspection["kind"] == "OBJECT"
+            del query_type_introspection["kind"]
+
+            with raises(
+                TypeError,
+                match=r"^Invalid or incomplete introspection result\."
+                " Ensure that a full introspection query is used"
+                r" in order to build a client schema: {'name': 'Query', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+        def throws_when_missing_interfaces():
+            introspection = introspection_from_schema(dummy_schema)
+
+            query_type_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "Query"
+            )
+            assert query_type_introspection["interfaces"] == []
+            del query_type_introspection["interfaces"]
+
+            with raises(
+                TypeError,
+                match="^Query interfaces cannot be resolved."
+                " Introspection result missing interfaces:"
+                r" {'kind': 'OBJECT', 'name': 'Query', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+        def legacy_support_for_interfaces_with_null_as_interfaces_field():
+            introspection = introspection_from_schema(dummy_schema)
+            some_interface_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "SomeInterface"
+            )
+
+            assert some_interface_introspection["interfaces"] == []
+            some_interface_introspection["interfaces"] = None
+
+            client_schema = build_client_schema(introspection)
+            assert print_schema(client_schema) == print_schema(dummy_schema)
+
+        def throws_when_missing_fields():
+            introspection = introspection_from_schema(dummy_schema)
+            query_type_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "Query"
+            )
+
+            assert query_type_introspection["fields"]
+            del query_type_introspection["fields"]
+
+            with raises(
+                TypeError,
+                match="^Query fields cannot be resolved."
+                " Introspection result missing fields:"
+                r" {'kind': 'OBJECT', 'name': 'Query', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+        def throws_when_missing_field_args():
+            introspection = introspection_from_schema(dummy_schema)
+
+            query_type_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "Query"
+            )
+            assert query_type_introspection["fields"][0]["args"]
+            del query_type_introspection["fields"][0]["args"]
+
+            with raises(
+                TypeError,
+                match="^Query fields cannot be resolved."
+                r" Introspection result missing field args: {'name': 'foo', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+        def throws_when_output_type_is_used_as_an_arg_type():
+            introspection = introspection_from_schema(dummy_schema)
+
+            query_type_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "Query"
+            )
+            assert (
+                query_type_introspection["fields"][0]["args"][0]["type"]["name"]
+                == "String"
+            )
+            query_type_introspection["fields"][0]["args"][0]["type"][
+                "name"
+            ] = "SomeUnion"
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+
+            assert str(exc_info.value).startswith(
+                "Query fields cannot be resolved."
+                " Introspection must provide input type for arguments,"
+                " but received: SomeUnion."
+            )
+
+        def throws_when_output_type_is_used_as_an_input_value_type():
+            introspection = introspection_from_schema(dummy_schema)
+
+            input_object_type_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "SomeInputObject"
+            )
+            assert (
+                input_object_type_introspection["inputFields"][0]["type"]["name"]
+                == "String"
+            )
+            input_object_type_introspection["inputFields"][0]["type"][
+                "name"
+            ] = "SomeUnion"
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+
+            assert str(exc_info.value).startswith(
+                "SomeInputObject fields cannot be resolved."
+                " Introspection must provide input type for input fields,"
+                " but received: SomeUnion."
+            )
+
+        def throws_when_input_type_is_used_as_a_field_type():
+            introspection = introspection_from_schema(dummy_schema)
+
+            query_type_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "Query"
+            )
+            assert query_type_introspection["fields"][0]["type"]["name"] == "String"
+            query_type_introspection["fields"][0]["type"]["name"] = "SomeInputObject"
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+
+            assert str(exc_info.value).startswith(
+                "Query fields cannot be resolved."
+                " Introspection must provide output type for fields,"
+                " but received: SomeInputObject."
+            )
+
+        def throws_when_missing_possible_types():
+            introspection = introspection_from_schema(dummy_schema)
+
+            some_union_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "SomeUnion"
+            )
+            assert some_union_introspection["possibleTypes"]
+            del some_union_introspection["possibleTypes"]
+
+            with raises(
+                TypeError,
+                match="^Introspection result missing possibleTypes:"
+                r" {'kind': 'UNION', 'name': 'SomeUnion', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+        def throws_when_missing_enum_values():
+            introspection = introspection_from_schema(dummy_schema)
+
+            some_enum_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "SomeEnum"
+            )
+            assert some_enum_introspection["enumValues"]
+            del some_enum_introspection["enumValues"]
+
+            with raises(
+                TypeError,
+                match="^Introspection result missing enumValues:"
+                r" {'kind': 'ENUM', 'name': 'SomeEnum', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+        def throws_when_missing_input_fields():
+            introspection = introspection_from_schema(dummy_schema)
+
+            some_input_object_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "SomeInputObject"
+            )
+            assert some_input_object_introspection["inputFields"]
+            del some_input_object_introspection["inputFields"]
+
+            with raises(
+                TypeError,
+                match="^Introspection result missing inputFields:"
+                r" {'kind': 'INPUT_OBJECT', 'name': 'SomeInputObject', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+        def throws_when_missing_directive_locations():
+            introspection = introspection_from_schema(dummy_schema)
+
+            some_directive_introspection = introspection["__schema"]["directives"][0]
+            assert some_directive_introspection["name"] == "SomeDirective"
+            assert some_directive_introspection["locations"] == ["QUERY"]
+            del some_directive_introspection["locations"]
+
+            with raises(
+                TypeError,
+                match="^Introspection result missing directive locations:"
+                r" {'name': 'SomeDirective', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+        def throws_when_missing_directive_args():
+            introspection = introspection_from_schema(dummy_schema)
+
+            some_directive_introspection = introspection["__schema"]["directives"][0]
+            assert some_directive_introspection["name"] == "SomeDirective"
+            assert some_directive_introspection["args"] == []
+            del some_directive_introspection["args"]
+
+            with raises(
+                TypeError,
+                match="^Introspection result missing directive args:"
+                r" {'name': 'SomeDirective', .*}\.$",
+            ):
+                build_client_schema(introspection)
+
+    def describe_very_deep_decorators_are_not_supported():
+        def fails_on_very_deep_lists_more_than_7_levels():
+            schema = build_schema(
+                """
+                type Query {
+                  foo: [[[[[[[[String]]]]]]]]
+                }
+                """
+            )
+
+            introspection = introspection_from_schema(schema)
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+
+            assert str(exc_info.value) == (
+                "Query fields cannot be resolved."
+                " Decorated type deeper than introspection query."
+            )
+
+        def fails_on_a_very_deep_non_null_more_than_7_levels():
+            schema = build_schema(
+                """
+                type Query {
+                  foo: [[[[String!]!]!]!]
+                }
+                """
+            )
+
+            introspection = introspection_from_schema(schema)
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+
+            assert str(exc_info.value) == (
+                "Query fields cannot be resolved."
+                " Decorated type deeper than introspection query."
+            )
+
+        def succeeds_on_deep_types_less_or_equal_7_levels():
+            # e.g., fully non-null 3D matrix
+            sdl = dedent(
+                """
+                type Query {
+                  foo: [[[String!]!]!]!
+                }
+                """
+            )
+
+            assert cycle_introspection(sdl) == sdl
+
+    def describe_prevents_infinite_recursion_on_invalid_introspection():
+        def recursive_interfaces():
+            sdl = """
+                type Query {
+                  foo: Foo
+                }
+
+                type Foo {
+                  foo: String
+                }
+                """
+            schema = build_schema(sdl, assume_valid=True)
+            introspection = introspection_from_schema(schema)
+
+            foo_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "Foo"
+            )
+            assert foo_introspection["interfaces"] == []
+            # we need to patch here since invalid interfaces cannot be built with Python
+            foo_introspection["interfaces"] = [
+                {"kind": "OBJECT", "name": "Foo", "ofType": None}
+            ]
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+            assert str(exc_info.value) == (
+                "Foo interfaces cannot be resolved."
+                " Expected Foo to be a GraphQL Interface type."
+            )
+
+        def recursive_union():
+            sdl = """
+                type Query {
+                  foo: Foo
+                }
+
+                union Foo
+                """
+            schema = build_schema(sdl, assume_valid=True)
+            introspection = introspection_from_schema(schema)
+
+            foo_introspection = next(
+                type_
+                for type_ in introspection["__schema"]["types"]
+                if type_["name"] == "Foo"
+            )
+            assert foo_introspection["kind"] == "UNION"
+            assert foo_introspection["possibleTypes"] == []
+            # we need to patch here since invalid unions cannot be built with Python
+            foo_introspection["possibleTypes"] = [
+                {"kind": "UNION", "name": "Foo", "ofType": None}
+            ]
+
+            with raises(TypeError) as exc_info:
+                build_client_schema(introspection)
+            assert str(exc_info.value) == (
+                "Foo types cannot be resolved."
+                " Expected Foo to be a GraphQL Object type."
+            )
diff --git a/tests/utilities/test_coerce_input_value.py b/tests/utilities/test_coerce_input_value.py
new file mode 100644
index 0000000..adcb4a3
--- /dev/null
+++ b/tests/utilities/test_coerce_input_value.py
@@ -0,0 +1,339 @@
+from math import nan
+from typing import Any, List, NamedTuple, Union
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.pyutils import Undefined
+from graphql.type import (
+    GraphQLEnumType,
+    GraphQLFloat,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLScalarType,
+)
+from graphql.utilities import coerce_input_value
+
+
+class CoercedValueError(NamedTuple):
+    error: str
+    path: List[Union[str, int]]
+    value: Any
+
+
+class CoercedValue(NamedTuple):
+    errors: List[CoercedValueError]
+    value: Any
+
+
+def expect_value(result: CoercedValue) -> Any:
+    assert result.errors == []
+    return result.value
+
+
+def expect_errors(result: CoercedValue) -> List[CoercedValueError]:
+    return result.errors
+
+
+def describe_coerce_input_value():
+    def _coerce_value(input_value, type_):
+        errors: List[CoercedValueError] = []
+        append = errors.append
+
+        def on_error(path, invalid_value, error):
+            append(CoercedValueError(error.message, path, invalid_value))
+
+        value = coerce_input_value(input_value, type_, on_error)
+        return CoercedValue(errors, value)
+
+    def describe_for_graphql_non_null():
+        TestNonNull = GraphQLNonNull(GraphQLInt)
+
+        def returns_non_error_for_non_null_value():
+            result = _coerce_value(1, TestNonNull)
+            assert expect_value(result) == 1
+
+        def returns_an_error_for_undefined_value():
+            result = _coerce_value(Undefined, TestNonNull)
+            assert expect_errors(result) == [
+                ("Expected non-nullable type 'Int!' not to be None.", [], Undefined)
+            ]
+
+        def returns_an_error_for_null_value():
+            result = _coerce_value(None, TestNonNull)
+            assert expect_errors(result) == [
+                ("Expected non-nullable type 'Int!' not to be None.", [], None)
+            ]
+
+    def describe_for_graphql_scalar():
+        def _parse_value(input_dict):
+            assert isinstance(input_dict, dict)
+            error = input_dict.get("error")
+            if error:
+                raise ValueError(error)
+            return input_dict.get("value")
+
+        TestScalar = GraphQLScalarType("TestScalar", parse_value=_parse_value)
+
+        def returns_no_error_for_valid_input():
+            result = _coerce_value({"value": 1}, TestScalar)
+            assert expect_value(result) == 1
+
+        def returns_no_error_for_null_result():
+            result = _coerce_value({"value": None}, TestScalar)
+            assert expect_value(result) is None
+
+        def returns_no_error_for_nan_result():
+            result = _coerce_value({"value": nan}, TestScalar)
+            assert expect_value(result) is nan
+
+        def returns_an_error_for_undefined_result():
+            result = _coerce_value({"value": Undefined}, TestScalar)
+            assert expect_errors(result) == [
+                ("Expected type 'TestScalar'.", [], {"value": Undefined})
+            ]
+
+        def returns_an_error_for_undefined_result_with_some_error_message():
+            input_value = {"error": "Some error message"}
+            result = _coerce_value(input_value, TestScalar)
+            assert expect_errors(result) == [
+                (
+                    "Expected type 'TestScalar'. Some error message",
+                    [],
+                    {"error": "Some error message"},
+                )
+            ]
+
+    def describe_for_graphql_enum():
+        TestEnum = GraphQLEnumType(
+            "TestEnum", {"FOO": "InternalFoo", "BAR": 123_456_789}
+        )
+
+        def returns_no_error_for_a_known_enum_name():
+            foo_result = _coerce_value("FOO", TestEnum)
+            assert expect_value(foo_result) == "InternalFoo"
+
+            bar_result = _coerce_value("BAR", TestEnum)
+            assert expect_value(bar_result) == 123_456_789
+
+        def returns_an_error_for_misspelled_enum_value():
+            result = _coerce_value("foo", TestEnum)
+            assert expect_errors(result) == [
+                (
+                    "Value 'foo' does not exist in 'TestEnum' enum."
+                    " Did you mean the enum value 'FOO'?",
+                    [],
+                    "foo",
+                )
+            ]
+
+        def returns_an_error_for_incorrect_value_type():
+            result1 = _coerce_value(123, TestEnum)
+            assert expect_errors(result1) == [
+                ("Enum 'TestEnum' cannot represent non-string value: 123.", [], 123)
+            ]
+
+            result2 = _coerce_value({"field": "value"}, TestEnum)
+            assert expect_errors(result2) == [
+                (
+                    "Enum 'TestEnum' cannot represent non-string value:"
+                    " {'field': 'value'}.",
+                    [],
+                    {"field": "value"},
+                )
+            ]
+
+    def describe_for_graphql_input_object():
+        TestInputObject = GraphQLInputObjectType(
+            "TestInputObject",
+            {
+                "foo": GraphQLInputField(GraphQLNonNull(GraphQLInt)),
+                "bar": GraphQLInputField(GraphQLInt),
+            },
+        )
+
+        def returns_no_error_for_a_valid_input():
+            result = _coerce_value({"foo": 123}, TestInputObject)
+            assert expect_value(result) == {"foo": 123}
+
+        def returns_an_error_for_a_non_dict_value():
+            result = _coerce_value(123, TestInputObject)
+            assert expect_errors(result) == [
+                ("Expected type 'TestInputObject' to be a dict.", [], 123)
+            ]
+
+        def returns_an_error_for_an_invalid_field():
+            result = _coerce_value({"foo": nan}, TestInputObject)
+            assert expect_errors(result) == [
+                ("Int cannot represent non-integer value: nan", ["foo"], nan,)
+            ]
+
+        def returns_multiple_errors_for_multiple_invalid_fields():
+            result = _coerce_value({"foo": "abc", "bar": "def"}, TestInputObject)
+            assert expect_errors(result) == [
+                ("Int cannot represent non-integer value: 'abc'", ["foo"], "abc",),
+                ("Int cannot represent non-integer value: 'def'", ["bar"], "def",),
+            ]
+
+        def returns_error_for_a_missing_required_field():
+            result = _coerce_value({"bar": 123}, TestInputObject)
+            assert expect_errors(result) == [
+                (
+                    "Field 'foo' of required type 'Int!' was not provided.",
+                    [],
+                    {"bar": 123},
+                )
+            ]
+
+        def returns_error_for_an_unknown_field():
+            result = _coerce_value({"foo": 123, "unknownField": 123}, TestInputObject)
+            assert expect_errors(result) == [
+                (
+                    "Field 'unknownField' is not defined by type 'TestInputObject'.",
+                    [],
+                    {"foo": 123, "unknownField": 123},
+                )
+            ]
+
+        def returns_error_for_a_misspelled_field():
+            result = _coerce_value({"foo": 123, "bart": 123}, TestInputObject)
+            assert expect_errors(result) == [
+                (
+                    "Field 'bart' is not defined by type 'TestInputObject'."
+                    " Did you mean 'bar'?",
+                    [],
+                    {"foo": 123, "bart": 123},
+                )
+            ]
+
+        def transforms_names_using_out_name():
+            # This is an extension of GraphQL.js.
+            ComplexInputObject = GraphQLInputObjectType(
+                "Complex",
+                {
+                    "realPart": GraphQLInputField(GraphQLFloat, out_name="real_part"),
+                    "imagPart": GraphQLInputField(
+                        GraphQLFloat, default_value=0, out_name="imag_part"
+                    ),
+                },
+            )
+            result = _coerce_value({"realPart": 1}, ComplexInputObject)
+            assert expect_value(result) == {"real_part": 1, "imag_part": 0}
+
+        def transforms_values_with_out_type():
+            # This is an extension of GraphQL.js.
+            ComplexInputObject = GraphQLInputObjectType(
+                "Complex",
+                {
+                    "real": GraphQLInputField(GraphQLFloat),
+                    "imag": GraphQLInputField(GraphQLFloat),
+                },
+                out_type=lambda value: complex(value["real"], value["imag"]),
+            )
+            result = _coerce_value({"real": 1, "imag": 2}, ComplexInputObject)
+            assert expect_value(result) == 1 + 2j
+
+    def describe_for_graphql_input_object_with_default_value():
+        def _get_test_input_object(default_value):
+            return GraphQLInputObjectType(
+                "TestInputObject",
+                {
+                    "foo": GraphQLInputField(
+                        GraphQLScalarType("TestScalar"), default_value=default_value
+                    )
+                },
+            )
+
+        def returns_no_errors_for_valid_input_value():
+            result = _coerce_value({"foo": 5}, _get_test_input_object(7))
+            assert expect_value(result) == {"foo": 5}
+
+        def returns_object_with_default_value():
+            result = _coerce_value({}, _get_test_input_object(7))
+            assert expect_value(result) == {"foo": 7}
+
+        def returns_null_as_value():
+            result = _coerce_value({}, _get_test_input_object(None))
+            assert expect_value(result) == {"foo": None}
+
+        def returns_nan_as_value():
+            result = _coerce_value({}, _get_test_input_object(nan))
+            result_value = expect_value(result)
+            assert "foo" in result_value
+            assert result_value["foo"] is nan
+
+    def describe_for_graphql_list():
+        TestList = GraphQLList(GraphQLInt)
+
+        def returns_no_error_for_a_valid_input():
+            result = _coerce_value([1, 2, 3], TestList)
+            assert expect_value(result) == [1, 2, 3]
+
+        def returns_an_error_for_an_invalid_input():
+            result = _coerce_value([1, "b", True, 4], TestList)
+            assert expect_errors(result) == [
+                ("Int cannot represent non-integer value: 'b'", [1], "b",),
+                ("Int cannot represent non-integer value: True", [2], True,),
+            ]
+
+        def returns_a_list_for_a_non_list_value():
+            result = _coerce_value(42, TestList)
+            assert expect_value(result) == [42]
+
+        def returns_a_list_for_a_non_list_invalid_value():
+            result = _coerce_value("Undefined", TestList)
+            assert expect_errors(result) == [
+                (
+                    "Int cannot represent non-integer value: 'Undefined'",
+                    [],
+                    "Undefined",
+                )
+            ]
+
+        def returns_null_for_a_null_value():
+            result = _coerce_value(None, TestList)
+            assert expect_value(result) is None
+
+    def describe_for_nested_graphql_list():
+        TestNestedList = GraphQLList(GraphQLList(GraphQLInt))
+
+        def returns_no_error_for_a_valid_input():
+            result = _coerce_value([[1], [2], [3]], TestNestedList)
+            assert expect_value(result) == [[1], [2], [3]]
+
+        def returns_a_list_for_a_non_list_value():
+            result = _coerce_value(42, TestNestedList)
+            assert expect_value(result) == [[42]]
+
+        def returns_null_for_a_null_value():
+            result = _coerce_value(None, TestNestedList)
+            assert expect_value(result) is None
+
+        def returns_nested_list_for_nested_non_list_values():
+            result = _coerce_value([1, 2, 3], TestNestedList)
+            assert expect_value(result) == [[1], [2], [3]]
+
+        def returns_nested_null_for_nested_null_values():
+            result = _coerce_value([42, [None], None], TestNestedList)
+            assert expect_value(result) == [[42], [None], None]
+
+    def describe_with_default_on_error():
+        def throw_error_without_path():
+            with raises(GraphQLError) as exc_info:
+                assert coerce_input_value(None, GraphQLNonNull(GraphQLInt))
+            assert exc_info.value.message == (
+                "Invalid value None: Expected non-nullable type 'Int!' not to be None."
+            )
+
+        def throw_error_with_path():
+            with raises(GraphQLError) as exc_info:
+                assert coerce_input_value(
+                    [None], GraphQLList(GraphQLNonNull(GraphQLInt))
+                )
+            assert exc_info.value.message == (
+                "Invalid value None at 'value[0]':"
+                " Expected non-nullable type 'Int!' not to be None."
+            )
diff --git a/tests/utilities/test_concat_ast.py b/tests/utilities/test_concat_ast.py
new file mode 100644
index 0000000..7d25d0b
--- /dev/null
+++ b/tests/utilities/test_concat_ast.py
@@ -0,0 +1,39 @@
+from graphql.language import parse, print_ast, Source
+from graphql.utilities import concat_ast
+
+from ..utils import dedent
+
+
+def describe_concat_ast():
+    def concatenates_two_asts_together():
+        source_a = Source(
+            """
+            { a, b, ... Frag }
+            """
+        )
+
+        source_b = Source(
+            """
+            fragment Frag on T {
+                c
+            }
+            """
+        )
+
+        ast_a = parse(source_a)
+        ast_b = parse(source_b)
+        ast_c = concat_ast([ast_a, ast_b])
+
+        assert print_ast(ast_c) == dedent(
+            """
+            {
+              a
+              b
+              ...Frag
+            }
+
+            fragment Frag on T {
+              c
+            }
+            """
+        )
diff --git a/tests/utilities/test_extend_schema.py b/tests/utilities/test_extend_schema.py
new file mode 100644
index 0000000..1d51d0d
--- /dev/null
+++ b/tests/utilities/test_extend_schema.py
@@ -0,0 +1,1489 @@
+from typing import Union
+
+from pytest import raises  # type: ignore
+
+from graphql import graphql_sync
+from graphql.language import (
+    parse,
+    print_ast,
+    DocumentNode,
+    StringValueNode,
+    TypeDefinitionNode,
+)
+from graphql.pyutils import FrozenList
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLFloat,
+    GraphQLID,
+    GraphQLInputField,
+    GraphQLInt,
+    GraphQLNamedType,
+    GraphQLSchema,
+    GraphQLString,
+    assert_directive,
+    assert_enum_type,
+    assert_input_object_type,
+    assert_interface_type,
+    assert_object_type,
+    assert_scalar_type,
+    assert_union_type,
+    validate_schema,
+)
+from graphql.utilities import (
+    build_schema,
+    concat_ast,
+    extend_schema,
+    get_description,
+    print_schema,
+)
+
+from ..utils import dedent
+
+TypeWithAstNode = Union[
+    GraphQLArgument,
+    GraphQLEnumValue,
+    GraphQLField,
+    GraphQLInputField,
+    GraphQLNamedType,
+    GraphQLSchema,
+]
+
+TypeWithExtensionAstNodes = Union[
+    GraphQLNamedType, GraphQLSchema,
+]
+
+
+def print_extension_nodes(obj: TypeWithExtensionAstNodes) -> str:
+    assert obj is not None and obj.extension_ast_nodes is not None
+    return print_ast(DocumentNode(definitions=obj.extension_ast_nodes))
+
+
+def print_schema_changes(schema: GraphQLSchema, extended_schema: GraphQLSchema) -> str:
+    schema_definitions = [
+        print_ast(definition) for definition in parse(print_schema(schema)).definitions
+    ]
+    ast = parse(print_schema(extended_schema))
+    return print_ast(
+        DocumentNode(
+            definitions=FrozenList(
+                node
+                for node in ast.definitions
+                if print_ast(node) not in schema_definitions
+            )
+        )
+    )
+
+
+def print_ast_node(obj: TypeWithAstNode) -> str:
+    assert obj is not None and obj.ast_node is not None
+    return print_ast(obj.ast_node)
+
+
+def describe_extend_schema():
+    def returns_the_original_schema_when_there_are_no_type_definitions():
+        schema = build_schema("type Query")
+        extended_schema = extend_schema(schema, parse("{ field }"))
+        assert extended_schema == schema
+
+    def can_be_used_for_limited_execution():
+        schema = build_schema("type Query")
+        extend_ast = parse(
+            """
+            extend type Query {
+              newField: String
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        result = graphql_sync(
+            schema=extended_schema, source="{ newField }", root_value={"newField": 123}
+        )
+        assert result == ({"newField": "123"}, None)
+
+    def extends_objects_by_adding_new_fields():
+        schema = build_schema(
+            '''
+            type Query {
+              someObject: SomeObject
+            }
+
+            type SomeObject implements AnotherInterface & SomeInterface {
+              self: SomeObject
+              tree: [SomeObject]!
+              """Old field description."""
+              oldField: String
+            }
+
+            interface SomeInterface {
+              self: SomeInterface
+            }
+
+            interface AnotherInterface {
+              self: SomeObject
+            }
+            '''
+        )
+        extension_sdl = dedent(
+            '''
+            extend type SomeObject {
+              """New field description."""
+              newField(arg: Boolean): String
+            }
+          '''
+        )
+        extended_schema = extend_schema(schema, parse(extension_sdl))
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            '''
+            type SomeObject implements AnotherInterface & SomeInterface {
+              self: SomeObject
+              tree: [SomeObject]!
+              """Old field description."""
+              oldField: String
+              """New field description."""
+              newField(arg: Boolean): String
+            }
+            '''
+        )
+
+    def extends_objects_with_standard_type_fields():
+        schema = build_schema("type Query")
+
+        # Only String and Boolean are used by introspection types
+        assert schema.get_type("Int") is None
+        assert schema.get_type("Float") is None
+        assert schema.get_type("String") is GraphQLString
+        assert schema.get_type("Boolean") is GraphQLBoolean
+        assert schema.get_type("ID") is None
+
+        extend_ast = parse(
+            """
+            extend type Query {
+              bool: Boolean
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert extended_schema.get_type("Int") is None
+        assert extended_schema.get_type("Float") is None
+        assert extended_schema.get_type("String") is GraphQLString
+        assert extended_schema.get_type("Boolean") is GraphQLBoolean
+        assert extended_schema.get_type("ID") is None
+
+        extend_twice_ast = parse(
+            """
+            extend type Query {
+              int: Int
+              float: Float
+              id: ID
+            }
+            """
+        )
+        extended_twice_schema = extend_schema(schema, extend_twice_ast)
+
+        assert validate_schema(extended_twice_schema) == []
+        assert extended_twice_schema.get_type("Int") is GraphQLInt
+        assert extended_twice_schema.get_type("Float") is GraphQLFloat
+        assert extended_twice_schema.get_type("String") is GraphQLString
+        assert extended_twice_schema.get_type("Boolean") is GraphQLBoolean
+        assert extended_twice_schema.get_type("ID") is GraphQLID
+
+    def extends_enums_by_adding_new_values():
+        schema = build_schema(
+            '''
+            type Query {
+              someEnum(arg: SomeEnum): SomeEnum
+            }
+
+            directive @foo(arg: SomeEnum) on SCHEMA
+
+            enum SomeEnum {
+              """Old value description."""
+              OLD_VALUE
+            }
+            '''
+        )
+        extend_ast = parse(
+            '''
+            extend enum SomeEnum {
+              """New value description."""
+              NEW_VALUE
+            }
+            '''
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            '''
+            enum SomeEnum {
+              """Old value description."""
+              OLD_VALUE
+              """New value description."""
+              NEW_VALUE
+            }
+            '''
+        )
+
+    def extends_unions_by_adding_new_types():
+        schema = build_schema(
+            """
+            type Query {
+              someUnion: SomeUnion
+            }
+
+            union SomeUnion = Foo | Biz
+
+            type Foo { foo: String }
+            type Biz { biz: String }
+            type Bar { bar: String }
+            """
+        )
+        extend_ast = parse(
+            """
+            extend union SomeUnion = Bar
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            union SomeUnion = Foo | Biz | Bar
+            """
+        )
+
+    def allows_extension_of_union_by_adding_itself():
+        schema = build_schema(
+            """
+            union SomeUnion
+            """
+        )
+        extend_ast = parse(
+            """
+            extend union SomeUnion = SomeUnion
+            """
+        )
+        # invalid schema cannot be built with Python
+        with raises(TypeError) as exc_info:
+            extend_schema(schema, extend_ast)
+        assert str(exc_info.value) == (
+            "SomeUnion types must be specified"
+            " as a collection of GraphQLObjectType instances."
+        )
+
+    def extends_inputs_by_adding_new_fields():
+        schema = build_schema(
+            '''
+            type Query {
+              someInput(arg: SomeInput): String
+            }
+
+            directive @foo(arg: SomeInput) on SCHEMA
+
+            input SomeInput {
+              """Old field description."""
+              oldField: String
+            }
+            '''
+        )
+        extend_ast = parse(
+            '''
+            extend input SomeInput {
+              """New field description."""
+              newField: String
+            }
+            '''
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            '''
+            input SomeInput {
+              """Old field description."""
+              oldField: String
+              """New field description."""
+              newField: String
+            }
+            '''
+        )
+
+    def extends_scalars_by_adding_new_directives():
+        schema = build_schema(
+            """
+            type Query {
+              someScalar(arg: SomeScalar): SomeScalar
+            }
+
+            directive @foo(arg: SomeScalar) on SCALAR
+
+            input FooInput {
+              foo: SomeScalar
+            }
+
+            scalar SomeScalar
+            """
+        )
+        extension_sdl = dedent(
+            """
+            extend scalar SomeScalar @foo
+            """
+        )
+        extended_schema = extend_schema(schema, parse(extension_sdl))
+
+        assert validate_schema(extended_schema) == []
+
+        some_scalar = assert_scalar_type(extended_schema.get_type("SomeScalar"))
+        assert print_extension_nodes(some_scalar) == extension_sdl
+
+    def extends_scalars_by_adding_specified_by_directive():
+        schema = build_schema(
+            """
+            type Query {
+              foo: Foo
+            }
+
+            scalar Foo
+
+            directive @foo on SCALAR
+            """
+        )
+        extension_sdl = dedent(
+            """
+            extend scalar Foo @foo
+
+            extend scalar Foo @specifiedBy(url: "https://example.com/foo_spec")
+            """
+        )
+
+        extended_schema = extend_schema(schema, parse(extension_sdl))
+        foo = assert_scalar_type(extended_schema.get_type("Foo"))
+
+        assert foo.specified_by_url == "https://example.com/foo_spec"
+
+        assert validate_schema(extended_schema) == []
+        assert print_extension_nodes(foo) == extension_sdl
+
+    def correctly_assigns_ast_nodes_to_new_and_extended_types():
+        schema = build_schema(
+            """
+            type Query
+
+            scalar SomeScalar
+            enum SomeEnum
+            union SomeUnion
+            input SomeInput
+            type SomeObject
+            interface SomeInterface
+
+            directive @foo on SCALAR
+            """
+        )
+        first_extension_ast = parse(
+            """
+            extend type Query {
+              newField(testArg: TestInput): TestEnum
+            }
+
+            extend scalar SomeScalar @foo
+
+            extend enum SomeEnum {
+              NEW_VALUE
+            }
+
+            extend union SomeUnion = SomeObject
+
+            extend input SomeInput {
+              newField: String
+            }
+
+            extend interface SomeInterface {
+              newField: String
+            }
+
+            enum TestEnum {
+              TEST_VALUE
+            }
+
+            input TestInput {
+              testInputField: TestEnum
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, first_extension_ast)
+
+        second_extension_ast = parse(
+            """
+            extend type Query {
+              oneMoreNewField: TestUnion
+            }
+
+            extend scalar SomeScalar @test
+
+            extend enum SomeEnum {
+              ONE_MORE_NEW_VALUE
+            }
+
+            extend union SomeUnion = TestType
+
+            extend input SomeInput {
+              oneMoreNewField: String
+            }
+
+            extend interface SomeInterface {
+              oneMoreNewField: String
+            }
+
+            union TestUnion = TestType
+
+            interface TestInterface {
+              interfaceField: String
+            }
+
+            type TestType implements TestInterface {
+              interfaceField: String
+            }
+
+            directive @test(arg: Int) repeatable on FIELD | SCALAR
+            """
+        )
+        extended_twice_schema = extend_schema(extended_schema, second_extension_ast)
+
+        extend_in_one_go_schema = extend_schema(
+            schema, concat_ast([first_extension_ast, second_extension_ast])
+        )
+        assert print_schema(extend_in_one_go_schema) == print_schema(
+            extended_twice_schema
+        )
+
+        query = assert_object_type(extended_twice_schema.get_type("Query"))
+        some_enum = assert_enum_type(extended_twice_schema.get_type("SomeEnum"))
+        some_union = assert_union_type(extended_twice_schema.get_type("SomeUnion"))
+        some_scalar = assert_scalar_type(extended_twice_schema.get_type("SomeScalar"))
+        some_input = assert_input_object_type(
+            extended_twice_schema.get_type("SomeInput")
+        )
+        some_interface = assert_interface_type(
+            extended_twice_schema.get_type("SomeInterface")
+        )
+
+        test_input = assert_input_object_type(
+            extended_twice_schema.get_type("TestInput")
+        )
+        test_enum = assert_enum_type(extended_twice_schema.get_type("TestEnum"))
+        test_union = assert_union_type(extended_twice_schema.get_type("TestUnion"))
+        test_type = assert_object_type(extended_twice_schema.get_type("TestType"))
+        test_interface = assert_interface_type(
+            extended_twice_schema.get_type("TestInterface")
+        )
+        test_directive = assert_directive(extended_twice_schema.get_directive("test"))
+
+        assert test_type.extension_ast_nodes is None
+        assert test_enum.extension_ast_nodes is None
+        assert test_union.extension_ast_nodes is None
+        assert test_input.extension_ast_nodes is None
+        assert test_interface.extension_ast_nodes is None
+
+        assert query.extension_ast_nodes
+        assert len(query.extension_ast_nodes) == 2
+        assert some_scalar.extension_ast_nodes
+        assert len(some_scalar.extension_ast_nodes) == 2
+        assert some_enum.extension_ast_nodes
+        assert len(some_enum.extension_ast_nodes) == 2
+        assert some_union.extension_ast_nodes
+        assert len(some_union.extension_ast_nodes) == 2
+        assert some_input.extension_ast_nodes
+        assert len(some_input.extension_ast_nodes) == 2
+        assert some_interface.extension_ast_nodes
+        assert len(some_interface.extension_ast_nodes) == 2
+
+        assert {
+            test_input.ast_node,
+            test_enum.ast_node,
+            test_union.ast_node,
+            test_interface.ast_node,
+            test_type.ast_node,
+            test_directive.ast_node,
+            *query.extension_ast_nodes,
+            *some_scalar.extension_ast_nodes,
+            *some_enum.extension_ast_nodes,
+            *some_union.extension_ast_nodes,
+            *some_input.extension_ast_nodes,
+            *some_interface.extension_ast_nodes,
+        } == {*first_extension_ast.definitions, *second_extension_ast.definitions}
+
+        new_field = query.fields["newField"]
+        assert print_ast_node(new_field) == "newField(testArg: TestInput): TestEnum"
+        assert print_ast_node(new_field.args["testArg"]) == "testArg: TestInput"
+        assert (
+            print_ast_node(query.fields["oneMoreNewField"])
+            == "oneMoreNewField: TestUnion"
+        )
+
+        new_value = some_enum.values["NEW_VALUE"]
+        assert print_ast_node(new_value) == "NEW_VALUE"
+
+        one_more_new_value = some_enum.values["ONE_MORE_NEW_VALUE"]
+        assert print_ast_node(one_more_new_value) == "ONE_MORE_NEW_VALUE"
+        assert print_ast_node(some_input.fields["newField"]) == "newField: String"
+        assert (
+            print_ast_node(some_input.fields["oneMoreNewField"])
+            == "oneMoreNewField: String"
+        )
+        assert print_ast_node(some_interface.fields["newField"]) == "newField: String"
+        assert (
+            print_ast_node(some_interface.fields["oneMoreNewField"])
+            == "oneMoreNewField: String"
+        )
+
+        assert (
+            print_ast_node(test_input.fields["testInputField"])
+            == "testInputField: TestEnum"
+        )
+
+        test_value = test_enum.values["TEST_VALUE"]
+        assert print_ast_node(test_value) == "TEST_VALUE"
+
+        assert (
+            print_ast_node(test_interface.fields["interfaceField"])
+            == "interfaceField: String"
+        )
+        assert (
+            print_ast_node(test_type.fields["interfaceField"])
+            == "interfaceField: String"
+        )
+        assert print_ast_node(test_directive.args["arg"]) == "arg: Int"
+
+    def builds_types_with_deprecated_fields_and_values():
+        schema = GraphQLSchema()
+        extend_ast = parse(
+            """
+            type SomeObject {
+              deprecatedField: String @deprecated(reason: "not used anymore")
+            }
+
+            enum SomeEnum {
+              DEPRECATED_VALUE @deprecated(reason: "do not use")
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        some_type = assert_object_type(extended_schema.get_type("SomeObject"))
+        deprecated_field = some_type.fields["deprecatedField"]
+        assert deprecated_field.is_deprecated is True
+        assert deprecated_field.deprecation_reason == "not used anymore"
+
+        some_enum = assert_enum_type(extended_schema.get_type("SomeEnum"))
+        deprecated_enum = some_enum.values["DEPRECATED_VALUE"]
+        assert deprecated_enum.is_deprecated is True
+        assert deprecated_enum.deprecation_reason == "do not use"
+
+    def extends_objects_with_deprecated_fields():
+        schema = build_schema("type SomeObject")
+        extend_ast = parse(
+            """
+            extend type SomeObject {
+              deprecatedField: String @deprecated(reason: "not used anymore")
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        some_type = assert_object_type(extended_schema.get_type("SomeObject"))
+        deprecated_field = some_type.fields["deprecatedField"]
+        assert deprecated_field.is_deprecated is True
+        assert deprecated_field.deprecation_reason == "not used anymore"
+
+    def extend_enums_with_deprecated_values():
+        schema = build_schema("enum SomeEnum")
+        extend_ast = parse(
+            """
+            extend enum SomeEnum {
+              DEPRECATED_VALUE @deprecated(reason: "do not use")
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        some_enum = assert_enum_type(extended_schema.get_type("SomeEnum"))
+        deprecated_value = some_enum.values["DEPRECATED_VALUE"]
+        assert deprecated_value.is_deprecated is True
+        assert deprecated_value.deprecation_reason == "do not use"
+
+    def adds_new_unused_types():
+        schema = build_schema(
+            """
+            type Query {
+              dummy: String
+            }
+            """
+        )
+        extension_sdl = dedent(
+            """
+            type DummyUnionMember {
+              someField: String
+            }
+
+            enum UnusedEnum {
+              SOME_VALUE
+            }
+
+            input UnusedInput {
+              someField: String
+            }
+
+            interface UnusedInterface {
+              someField: String
+            }
+
+            type UnusedObject {
+              someField: String
+            }
+
+            union UnusedUnion = DummyUnionMember
+            """
+        )
+        extended_schema = extend_schema(schema, parse(extension_sdl))
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == extension_sdl
+
+    def extends_objects_by_adding_new_fields_with_arguments():
+        schema = build_schema(
+            """
+            type SomeObject
+
+            type Query {
+              someObject: SomeObject
+            }
+            """
+        )
+        extend_ast = parse(
+            """
+            input NewInputObj {
+              field1: Int
+              field2: [Float]
+              field3: String!
+            }
+
+            extend type SomeObject {
+              newField(arg1: String, arg2: NewInputObj!): String
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            type SomeObject {
+              newField(arg1: String, arg2: NewInputObj!): String
+            }
+
+            input NewInputObj {
+              field1: Int
+              field2: [Float]
+              field3: String!
+            }
+            """
+        )
+
+    def extends_objects_by_adding_new_fields_with_existing_types():
+        schema = build_schema(
+            """
+            type Query {
+              someObject: SomeObject
+            }
+
+            type SomeObject
+            enum SomeEnum { VALUE }
+            """
+        )
+        extend_ast = parse(
+            """
+            extend type SomeObject {
+              newField(arg1: SomeEnum!): SomeEnum
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            type SomeObject {
+              newField(arg1: SomeEnum!): SomeEnum
+            }
+            """
+        )
+
+    def extends_objects_by_adding_implemented_interfaces():
+        schema = build_schema(
+            """
+            type Query {
+              someObject: SomeObject
+            }
+
+            type SomeObject {
+              foo: String
+            }
+
+            interface SomeInterface {
+              foo: String
+            }
+            """
+        )
+        extend_ast = parse(
+            """
+            extend type SomeObject implements SomeInterface
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            type SomeObject implements SomeInterface {
+              foo: String
+            }
+            """
+        )
+
+    def extends_objects_by_including_new_types():
+        schema = build_schema(
+            """
+            type Query {
+              someObject: SomeObject
+            }
+
+            type SomeObject {
+              oldField: String
+            }
+            """
+        )
+        new_types_sdl = """
+            enum NewEnum {
+              VALUE
+            }
+
+            interface NewInterface {
+              baz: String
+            }
+
+            type NewObject implements NewInterface {
+              baz: String
+            }
+
+            scalar NewScalar
+
+            union NewUnion = NewObject
+            """
+        extend_ast = parse(
+            new_types_sdl
+            + """
+            extend type SomeObject {
+              newObject: NewObject
+              newInterface: NewInterface
+              newUnion: NewUnion
+              newScalar: NewScalar
+              newEnum: NewEnum
+              newTree: [SomeObject]!
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            type SomeObject {
+              oldField: String
+              newObject: NewObject
+              newInterface: NewInterface
+              newUnion: NewUnion
+              newScalar: NewScalar
+              newEnum: NewEnum
+              newTree: [SomeObject]!
+            }\n"""
+            + new_types_sdl
+        )
+
+    def extends_objects_by_adding_implemented_new_interfaces():
+        schema = build_schema(
+            """
+            type Query {
+              someObject: SomeObject
+            }
+
+            type SomeObject implements OldInterface {
+              oldField: String
+            }
+
+            interface OldInterface {
+              oldField: String
+            }
+            """
+        )
+        extend_ast = parse(
+            """
+            extend type SomeObject implements NewInterface {
+              newField: String
+            }
+
+            interface NewInterface {
+              newField: String
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            type SomeObject implements OldInterface & NewInterface {
+              oldField: String
+              newField: String
+            }
+
+            interface NewInterface {
+              newField: String
+            }
+            """
+        )
+
+    def extends_different_types_multiple_times():
+        schema = build_schema(
+            """
+            type Query {
+              someScalar: SomeScalar
+              someObject(someInput: SomeInput): SomeObject
+              someInterface: SomeInterface
+              someEnum: SomeEnum
+              someUnion: SomeUnion
+            }
+
+            scalar SomeScalar
+
+            type SomeObject implements SomeInterface {
+              oldField: String
+            }
+
+            interface SomeInterface {
+              oldField: String
+            }
+
+            enum SomeEnum {
+              OLD_VALUE
+            }
+
+            union SomeUnion = SomeObject
+
+            input SomeInput {
+              oldField: String
+            }
+            """
+        )
+        new_types_sdl = dedent(
+            """
+            scalar NewScalar
+
+            scalar AnotherNewScalar
+
+            type NewObject {
+              foo: String
+            }
+
+            type AnotherNewObject {
+              foo: String
+            }
+
+            interface NewInterface {
+              newField: String
+            }
+
+            interface AnotherNewInterface {
+              anotherNewField: String
+            }
+            """
+        )
+        schema_with_new_types = extend_schema(schema, parse(new_types_sdl))
+        assert print_schema_changes(schema, schema_with_new_types) == new_types_sdl
+
+        extend_ast = parse(
+            """
+            extend scalar SomeScalar @specifiedBy(url: "http://example.com/foo_spec")
+
+            extend type SomeObject implements NewInterface {
+                newField: String
+            }
+
+            extend type SomeObject implements AnotherNewInterface {
+                anotherNewField: String
+            }
+
+            extend enum SomeEnum {
+                NEW_VALUE
+            }
+
+            extend enum SomeEnum {
+                ANOTHER_NEW_VALUE
+            }
+
+            extend union SomeUnion = NewObject
+
+            extend union SomeUnion = AnotherNewObject
+
+            extend input SomeInput {
+                newField: String
+            }
+
+            extend input SomeInput {
+                anotherNewField: String
+            }
+            """
+        )
+        extended_schema = extend_schema(schema_with_new_types, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert (
+            print_schema_changes(schema, extended_schema)
+            == dedent(
+                """
+            scalar SomeScalar @specifiedBy(url: "http://example.com/foo_spec")
+
+            type SomeObject implements SomeInterface & NewInterface & AnotherNewInterface {
+              oldField: String
+              newField: String
+              anotherNewField: String
+            }
+
+            enum SomeEnum {
+              OLD_VALUE
+              NEW_VALUE
+              ANOTHER_NEW_VALUE
+            }
+
+            union SomeUnion = SomeObject | NewObject | AnotherNewObject
+
+            input SomeInput {
+              oldField: String
+              newField: String
+              anotherNewField: String
+            }
+
+            """  # noqa: E501
+            )
+            + new_types_sdl
+        )
+
+    def extends_interfaces_by_adding_new_fields():
+        schema = build_schema(
+            """
+            interface SomeInterface {
+              oldField: String
+            }
+
+            interface AnotherInterface implements SomeInterface {
+              oldField: String
+            }
+
+            type SomeObject implements SomeInterface & AnotherInterface {
+              oldField: String
+            }
+
+            type Query {
+              someInterface: SomeInterface
+            }
+            """
+        )
+        extend_ast = parse(
+            """
+            extend interface SomeInterface {
+              newField: String
+            }
+
+            extend interface AnotherInterface {
+              newField: String
+            }
+
+            extend type SomeObject {
+              newField: String
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            interface SomeInterface {
+              oldField: String
+              newField: String
+            }
+
+            interface AnotherInterface implements SomeInterface {
+              oldField: String
+              newField: String
+            }
+
+            type SomeObject implements SomeInterface & AnotherInterface {
+              oldField: String
+              newField: String
+            }
+            """
+        )
+
+    def extends_interfaces_by_adding_new_implemented_interfaces():
+        schema = build_schema(
+            """
+            interface SomeInterface {
+              oldField: String
+            }
+
+            interface AnotherInterface implements SomeInterface {
+              oldField: String
+            }
+
+            type SomeObject implements SomeInterface & AnotherInterface {
+              oldField: String
+            }
+
+            type Query {
+              someInterface: SomeInterface
+            }
+            """
+        )
+        extend_ast = parse(
+            """
+            interface NewInterface {
+              newField: String
+            }
+
+            extend interface AnotherInterface implements NewInterface {
+              newField: String
+            }
+
+            extend type SomeObject implements NewInterface {
+              newField: String
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            interface AnotherInterface implements SomeInterface & NewInterface {
+              oldField: String
+              newField: String
+            }
+
+            type SomeObject implements SomeInterface & AnotherInterface & NewInterface {
+              oldField: String
+              newField: String
+            }
+
+            interface NewInterface {
+              newField: String
+            }
+            """
+        )
+
+    def allows_extension_of_interface_with_missing_object_fields():
+        schema = build_schema(
+            """
+            type Query {
+              someInterface: SomeInterface
+            }
+
+            type SomeObject implements SomeInterface {
+              oldField: SomeInterface
+            }
+
+            interface SomeInterface {
+              oldField: SomeInterface
+            }
+            """
+        )
+        extend_ast = parse(
+            """
+            extend interface SomeInterface {
+              newField: String
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema)
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            interface SomeInterface {
+              oldField: SomeInterface
+              newField: String
+            }
+            """
+        )
+
+    def extends_interfaces_multiple_times():
+        schema = build_schema(
+            """
+            type Query {
+              someInterface: SomeInterface
+            }
+
+            interface SomeInterface {
+              some: SomeInterface
+            }
+            """
+        )
+        extend_ast = parse(
+            """
+            extend interface SomeInterface {
+              newFieldA: Int
+            }
+
+            extend interface SomeInterface {
+              newFieldB(test: Boolean): String
+            }
+            """
+        )
+        extended_schema = extend_schema(schema, extend_ast)
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == dedent(
+            """
+            interface SomeInterface {
+              some: SomeInterface
+              newFieldA: Int
+              newFieldB(test: Boolean): String
+            }
+            """
+        )
+
+    def may_extend_mutations_and_subscriptions():
+        mutation_schema = build_schema(
+            """
+            type Query {
+              queryField: String
+            }
+
+            type Mutation {
+              mutationField: String
+            }
+
+            type Subscription {
+              subscriptionField: String
+            }
+            """
+        )
+        ast = parse(
+            """
+            extend type Query {
+              newQueryField: Int
+            }
+
+            extend type Mutation {
+              newMutationField: Int
+            }
+
+            extend type Subscription {
+              newSubscriptionField: Int
+            }
+            """
+        )
+        original_print = print_schema(mutation_schema)
+        extended_schema = extend_schema(mutation_schema, ast)
+        assert extended_schema != mutation_schema
+        assert print_schema(mutation_schema) == original_print
+        assert print_schema(extended_schema) == dedent(
+            """
+            type Query {
+              queryField: String
+              newQueryField: Int
+            }
+
+            type Mutation {
+              mutationField: String
+              newMutationField: Int
+            }
+
+            type Subscription {
+              subscriptionField: String
+              newSubscriptionField: Int
+            }
+            """
+        )
+
+    def may_extend_directives_with_new_directive():
+        schema = build_schema(
+            """
+            type Query {
+              foo: String
+            }
+            """
+        )
+        extension_sdl = dedent(
+            '''
+            """New directive."""
+            directive @new(enable: Boolean!, tag: String) repeatable on QUERY | FIELD
+            '''
+        )
+        extended_schema = extend_schema(schema, parse(extension_sdl))
+
+        assert validate_schema(extended_schema) == []
+        assert print_schema_changes(schema, extended_schema) == extension_sdl
+
+    def rejects_invalid_sdl():
+        schema = GraphQLSchema()
+        extend_ast = parse("extend schema @unknown")
+
+        with raises(TypeError) as exc_info:
+            extend_schema(schema, extend_ast)
+        assert str(exc_info.value) == "Unknown directive '@unknown'."
+
+    def allows_to_disable_sdl_validation():
+        schema = GraphQLSchema()
+        extend_ast = parse("extend schema @unknown")
+
+        extend_schema(schema, extend_ast, assume_valid=True)
+        extend_schema(schema, extend_ast, assume_valid_sdl=True)
+
+    def throws_on_unknown_types():
+        schema = GraphQLSchema()
+        ast = parse(
+            """
+            type Query {
+              unknown: UnknownType
+            }
+            """
+        )
+        with raises(TypeError) as exc_info:
+            extend_schema(schema, ast, assume_valid_sdl=True)
+        assert str(exc_info.value).endswith("Unknown type: 'UnknownType'.")
+
+    def rejects_invalid_ast():
+        schema = GraphQLSchema()
+
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            extend_schema(schema, None)  # type: ignore
+        assert str(exc_info.value) == "Must provide valid Document AST."
+
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            extend_schema(schema, {})  # type: ignore
+        assert str(exc_info.value) == "Must provide valid Document AST."
+
+    def does_not_allow_replacing_a_default_directive():
+        schema = GraphQLSchema()
+        extend_ast = parse(
+            """
+            directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD
+            """
+        )
+
+        with raises(TypeError) as exc_info:
+            extend_schema(schema, extend_ast)
+        assert str(exc_info.value).startswith(
+            "Directive '@include' already exists in the schema."
+            " It cannot be redefined."
+        )
+
+    def does_not_allow_replacing_an_existing_enum_value():
+        schema = build_schema(
+            """
+            enum SomeEnum {
+              ONE
+            }
+            """
+        )
+        extend_ast = parse(
+            """
+            extend enum SomeEnum {
+              ONE
+            }
+            """
+        )
+
+        with raises(TypeError) as exc_info:
+            extend_schema(schema, extend_ast)
+        assert str(exc_info.value).startswith(
+            "Enum value 'SomeEnum.ONE' already exists in the schema."
+            " It cannot also be defined in this type extension."
+        )
+
+    def describe_can_add_additional_root_operation_types():
+        def does_not_automatically_include_common_root_type_names():
+            schema = GraphQLSchema()
+            extended_schema = extend_schema(schema, parse("type Mutation"))
+
+            assert extended_schema.get_type("Mutation")
+            assert extended_schema.mutation_type is None
+
+        def adds_schema_definition_missing_in_the_original_schema():
+            schema = build_schema(
+                """
+                directive @foo on SCHEMA
+                type Foo
+                """
+            )
+            assert schema.query_type is None
+
+            extension_sdl = dedent(
+                """
+                schema @foo {
+                  query: Foo
+                }
+                """
+            )
+            extended_schema = extend_schema(schema, parse(extension_sdl))
+
+            query_type = assert_object_type(extended_schema.query_type)
+            assert query_type.name == "Foo"
+            assert print_ast_node(extended_schema) + "\n" == extension_sdl
+
+        def adds_new_root_types_via_schema_extension():
+            schema = build_schema(
+                """
+                type Query
+                type MutationRoot
+                """
+            )
+            extension_sdl = dedent(
+                """
+                extend schema {
+                  mutation: MutationRoot
+                }
+                """
+            )
+            extended_schema = extend_schema(schema, parse(extension_sdl))
+
+            mutation_type = assert_object_type(extended_schema.mutation_type)
+            assert mutation_type.name == "MutationRoot"
+            assert print_extension_nodes(extended_schema) == extension_sdl
+
+        def adds_directive_via_schema_extension():
+            schema = build_schema(
+                """
+                type Query
+
+                directive @foo on SCHEMA
+                """
+            )
+            extension_sdl = dedent(
+                """
+                extend schema @foo
+                """
+            )
+            extended_schema = extend_schema(schema, parse(extension_sdl))
+
+            assert print_extension_nodes(extended_schema) == extension_sdl
+
+        def adds_multiple_new_root_types_via_schema_extension():
+            schema = build_schema("type Query")
+            extend_ast = parse(
+                """
+                extend schema {
+                  mutation: Mutation
+                  subscription: Subscription
+                }
+
+                type Mutation
+                type Subscription
+                """
+            )
+            extended_schema = extend_schema(schema, extend_ast)
+
+            mutation_type = assert_object_type(extended_schema.mutation_type)
+            assert mutation_type.name == "Mutation"
+
+            subscription_type = assert_object_type(extended_schema.subscription_type)
+            assert subscription_type.name == "Subscription"
+
+        def applies_multiple_schema_extensions():
+            schema = build_schema("type Query")
+            extend_ast = parse(
+                """
+                extend schema {
+                  mutation: Mutation
+                }
+                type Mutation
+
+                extend schema {
+                  subscription: Subscription
+                }
+                type Subscription
+                """
+            )
+            extended_schema = extend_schema(schema, extend_ast)
+
+            mutation_type = assert_object_type(extended_schema.mutation_type)
+            assert mutation_type.name == "Mutation"
+
+            subscription_type = assert_object_type(extended_schema.subscription_type)
+            assert subscription_type.name == "Subscription"
+
+        def schema_extension_ast_are_available_from_schema_object():
+            schema = build_schema(
+                """
+                type Query
+
+                directive @foo on SCHEMA
+                """
+            )
+            extend_ast = parse(
+                """
+                extend schema {
+                  mutation: Mutation
+                }
+                type Mutation
+
+                extend schema {
+                  subscription: Subscription
+                }
+                type Subscription
+                """
+            )
+            extended_schema = extend_schema(schema, extend_ast)
+
+            second_extend_ast = parse("extend schema @foo")
+            extended_twice_schema = extend_schema(extended_schema, second_extend_ast)
+
+            assert print_extension_nodes(extended_twice_schema) == dedent(
+                """
+                extend schema {
+                  mutation: Mutation
+                }
+
+                extend schema {
+                  subscription: Subscription
+                }
+
+                extend schema @foo
+                """
+            )
+
+
+def describe_get_description():
+    def returns_description_of_type_definition_node():
+        assert (
+            get_description(
+                TypeDefinitionNode(
+                    description=StringValueNode(value="This is a type definition")
+                )
+            )
+            == "This is a type definition"
+        )
+
+    def returns_none_for_node_without_description():
+        assert get_description(StringValueNode(value="Just a string value")) is None
diff --git a/tests/utilities/test_find_breaking_changes.py b/tests/utilities/test_find_breaking_changes.py
new file mode 100644
index 0000000..f0ad2ab
--- /dev/null
+++ b/tests/utilities/test_find_breaking_changes.py
@@ -0,0 +1,1278 @@
+from graphql.type import (
+    GraphQLSchema,
+    GraphQLDeprecatedDirective,
+    GraphQLIncludeDirective,
+    GraphQLSkipDirective,
+    GraphQLSpecifiedByDirective,
+)
+from graphql.utilities import (
+    BreakingChangeType,
+    DangerousChangeType,
+    build_schema,
+    find_breaking_changes,
+    find_dangerous_changes,
+)
+
+
+def describe_find_breaking_changes():
+    def should_detect_if_a_type_was_removed_or_not():
+        old_schema = build_schema(
+            """
+            type Type1
+            type Type2
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type Type2
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (BreakingChangeType.TYPE_REMOVED, "Type1 was removed.")
+        ]
+        assert find_breaking_changes(old_schema, old_schema) == []
+
+    def should_detect_if_a_standard_scalar_was_removed():
+        old_schema = build_schema(
+            """
+            type Query {
+              foo: Float
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type Query {
+              foo: String
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.TYPE_REMOVED,
+                "Standard scalar Float was removed"
+                " because it is not referenced anymore.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Query.foo changed type from Float to String.",
+            ),
+        ]
+
+    def should_detect_if_a_type_changed_its_type():
+        old_schema = build_schema(
+            """
+            scalar TypeWasScalarBecomesEnum
+            interface TypeWasInterfaceBecomesUnion
+            type TypeWasObjectBecomesInputObject
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            enum TypeWasScalarBecomesEnum
+            union TypeWasInterfaceBecomesUnion
+           input TypeWasObjectBecomesInputObject
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.TYPE_CHANGED_KIND,
+                "TypeWasScalarBecomesEnum changed from a Scalar type to an Enum type.",
+            ),
+            (
+                BreakingChangeType.TYPE_CHANGED_KIND,
+                "TypeWasInterfaceBecomesUnion changed"
+                " from an Interface type to a Union type.",
+            ),
+            (
+                BreakingChangeType.TYPE_CHANGED_KIND,
+                "TypeWasObjectBecomesInputObject changed"
+                " from an Object type to an Input type.",
+            ),
+        ]
+
+    def should_detect_if_a_field_on_type_was_deleted_or_changed_type():
+        old_schema = build_schema(
+            """
+            type TypeA
+            type TypeB
+
+            interface Type1 {
+              field1: TypeA
+              field2: String
+              field3: String
+              field4: TypeA
+              field6: String
+              field7: [String]
+              field8: Int
+              field9: Int!
+              field10: [Int]!
+              field11: Int
+              field12: [Int]
+              field13: [Int!]
+              field14: [Int]
+              field15: [[Int]]
+              field16: Int!
+              field17: [Int]
+              field18: [[Int!]!]
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type TypeA
+            type TypeB
+
+            interface Type1 {
+              field1: TypeA
+              field3: Boolean
+              field4: TypeB
+              field5: String
+              field6: [String]
+              field7: String
+              field8: Int!
+              field9: Int
+              field10: [Int]
+              field11: [Int]!
+              field12: [Int!]
+              field13: [Int]
+              field14: [[Int]]
+              field15: [Int]
+              field16: [Int]!
+              field17: [Int]!
+              field18: [[Int!]]
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (BreakingChangeType.FIELD_REMOVED, "Type1.field2 was removed."),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field3 changed type from String to Boolean.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field4 changed type from TypeA to TypeB.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field6 changed type from String to [String].",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field7 changed type from [String] to String.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field9 changed type from Int! to Int.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field10 changed type from [Int]! to [Int].",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field11 changed type from Int to [Int]!.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field13 changed type from [Int!] to [Int].",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field14 changed type from [Int] to [[Int]].",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field15 changed type from [[Int]] to [Int].",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field16 changed type from Int! to [Int]!.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "Type1.field18 changed type from [[Int!]!] to [[Int!]].",
+            ),
+        ]
+
+    def should_detect_if_fields_on_input_types_changed_kind_or_were_removed():
+        old_schema = build_schema(
+            """
+            input InputType1 {
+              field1: String
+              field2: Boolean
+              field3: [String]
+              field4: String!
+              field5: String
+              field6: [Int]
+              field7: [Int]!
+              field8: Int
+              field9: [Int]
+              field10: [Int!]
+              field11: [Int]
+              field12: [[Int]]
+              field13: Int!
+              field14: [[Int]!]
+              field15: [[Int]!]
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            input InputType1 {
+              field1: Int
+              field3: String
+              field4: String
+              field5: String!
+              field6: [Int]!
+              field7: [Int]
+              field8: [Int]!
+              field9: [Int!]
+              field10: [Int]
+              field11: [[Int]]
+              field12: [Int]
+              field13: [Int]!
+              field14: [[Int]]
+              field15: [[Int!]!]
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (BreakingChangeType.FIELD_REMOVED, "InputType1.field2 was removed."),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field1 changed type from String to Int.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field3 changed type from [String] to String.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field5 changed type from String to String!.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field6 changed type from [Int] to [Int]!.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field8 changed type from Int to [Int]!.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field9 changed type from [Int] to [Int!].",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field11 changed type from [Int] to [[Int]].",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field12 changed type from [[Int]] to [Int].",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field13 changed type from Int! to [Int]!.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "InputType1.field15 changed type from [[Int]!] to [[Int!]!].",
+            ),
+        ]
+
+    def should_detect_if_a_required_field_is_added_to_an_input_type():
+        old_schema = build_schema(
+            """
+            input InputType1 {
+              field1: String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            input InputType1 {
+                field1: String
+                requiredField: Int!
+                optionalField1: Boolean
+                optionalField2: Boolean! = false
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.REQUIRED_INPUT_FIELD_ADDED,
+                "A required field requiredField on input type InputType1 was added.",
+            )
+        ]
+
+    def should_detect_if_a_type_was_removed_from_a_union_type():
+        old_schema = build_schema(
+            """
+            type Type1
+            type Type2
+            type Type3
+
+            union UnionType1 = Type1 | Type2
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type Type1
+            type Type2
+            type Type3
+
+            union UnionType1 = Type1 | Type3
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.TYPE_REMOVED_FROM_UNION,
+                "Type2 was removed from union type UnionType1.",
+            )
+        ]
+
+    def should_detect_if_a_value_was_removed_from_an_enum_type():
+        old_schema = build_schema(
+            """
+            enum EnumType1 {
+              VALUE0
+              VALUE1
+              VALUE2
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            enum EnumType1 {
+              VALUE0
+              VALUE2
+              VALUE3
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.VALUE_REMOVED_FROM_ENUM,
+                "VALUE1 was removed from enum type EnumType1.",
+            )
+        ]
+
+    def should_detect_if_a_field_argument_was_removed():
+        old_schema = build_schema(
+            """
+            interface Interface1 {
+              field1(arg1: Boolean, objectArg: String): String
+            }
+
+            type Type1 {
+              field1(name: String): String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            interface Interface1 {
+              field1: String
+            }
+
+            type Type1 {
+              field1: String
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (BreakingChangeType.ARG_REMOVED, "Interface1.field1 arg arg1 was removed."),
+            (
+                BreakingChangeType.ARG_REMOVED,
+                "Interface1.field1 arg objectArg was removed.",
+            ),
+            (BreakingChangeType.ARG_REMOVED, "Type1.field1 arg name was removed."),
+        ]
+
+    def should_detect_if_a_field_argument_has_changed_type():
+        old_schema = build_schema(
+            """
+            type Type1 {
+              field1(
+                arg1: String
+                arg2: String
+                arg3: [String]
+                arg4: String
+                arg5: String!
+                arg6: String!
+                arg7: [Int]!
+                arg8: Int
+                arg9: [Int]
+                arg10: [Int!]
+                arg11: [Int]
+                arg12: [[Int]]
+                arg13: Int!
+                arg14: [[Int]!]
+                arg15: [[Int]!]
+              ): String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type Type1 {
+              field1(
+                arg1: Int
+                arg2: [String]
+                arg3: String
+                arg4: String!
+                arg5: Int
+                arg6: Int!
+                arg7: [Int]
+                arg8: [Int]!
+                arg9: [Int!]
+                arg10: [Int]
+                arg11: [[Int]]
+                arg12: [Int]
+                arg13: [Int]!
+                arg14: [[Int]]
+                arg15: [[Int!]!]
+               ): String
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg1 has changed type from String to Int.",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg2 has changed type from String to [String].",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg3 has changed type from [String] to String.",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg4 has changed type from String to String!.",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg5 has changed type from String! to Int.",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg6 has changed type from String! to Int!.",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg8 has changed type from Int to [Int]!.",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg9 has changed type from [Int] to [Int!].",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg11 has changed type from [Int] to [[Int]].",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg12 has changed type from [[Int]] to [Int].",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg13 has changed type from Int! to [Int]!.",
+            ),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "Type1.field1 arg arg15 has changed type from [[Int]!] to [[Int!]!].",
+            ),
+        ]
+
+    def should_detect_if_a_required_field_argument_was_added():
+        old_schema = build_schema(
+            """
+            type Type1 {
+              field1(arg1: String): String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type Type1 {
+              field1(
+                arg1: String,
+                newRequiredArg: String!
+                newOptionalArg1: Int
+                newOptionalArg2: Int! = 0
+              ): String
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.REQUIRED_ARG_ADDED,
+                "A required arg newRequiredArg on Type1.field1 was added.",
+            )
+        ]
+
+    def should_not_flag_args_with_the_same_type_signature_as_breaking():
+        old_schema = build_schema(
+            """
+            input InputType1 {
+              field1: String
+            }
+
+            type Type1 {
+              field1(arg1: Int!, arg2: InputType1): Int
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            input InputType1 {
+              field1: String
+            }
+
+            type Type1 {
+              field1(arg1: Int!, arg2: InputType1): Int
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == []
+
+    def should_consider_args_that_move_away_from_non_null_as_non_breaking():
+        old_schema = build_schema(
+            """
+            type Type1 {
+              field1(name: String!): String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type Type1 {
+              field1(name: String): String
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == []
+
+    def should_detect_interfaces_removed_from_types():
+        old_schema = build_schema(
+            """
+            interface Interface1
+
+            type Type1 implements Interface1
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            interface Interface1
+
+            type Type1
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.IMPLEMENTED_INTERFACE_REMOVED,
+                "Type1 no longer implements interface Interface1.",
+            )
+        ]
+
+    def should_detect_intrefaces_removed_from_interfaces():
+        old_schema = build_schema(
+            """
+            interface Interface1
+
+            interface Interface2 implements Interface1
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            interface Interface1
+
+            interface Interface2
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.IMPLEMENTED_INTERFACE_REMOVED,
+                "Interface2 no longer implements interface Interface1.",
+            )
+        ]
+
+    def should_ignore_changes_in_order_of_interfaces():
+        old_schema = build_schema(
+            """
+            interface FirstInterface
+            interface SecondInterface
+
+            type Type1 implements FirstInterface & SecondInterface
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            interface FirstInterface
+            interface SecondInterface
+
+            type Type1 implements SecondInterface & FirstInterface
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == []
+
+    def should_detect_all_breaking_changes():
+        old_schema = build_schema(
+            """
+            directive @DirectiveThatIsRemoved on FIELD_DEFINITION
+
+            directive @DirectiveThatRemovesArg(arg1: String) on FIELD_DEFINITION
+
+            directive @NonNullDirectiveAdded on FIELD_DEFINITION
+
+            directive @DirectiveThatWasRepeatable repeatable on FIELD_DEFINITION
+
+            directive @DirectiveName on FIELD_DEFINITION | QUERY
+
+            type ArgThatChanges {
+                field1(id: Float): String
+            }
+
+            enum EnumTypeThatLosesAValue {
+                VALUE0
+                VALUE1
+                VALUE2
+            }
+
+            interface Interface1
+            type TypeThatLoosesInterface1 implements Interface1
+
+            type TypeInUnion1
+            type TypeInUnion2
+            union UnionTypeThatLosesAType = TypeInUnion1 | TypeInUnion2
+
+            type TypeThatChangesType
+
+            type TypeThatGetsRemoved
+
+            interface TypeThatHasBreakingFieldChanges {
+                field1: String
+                field2: String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            directive @DirectiveThatRemovesArg on FIELD_DEFINITION
+
+            directive @NonNullDirectiveAdded(arg1: Boolean!) on FIELD_DEFINITION
+
+            directive @DirectiveThatWasRepeatable on FIELD_DEFINITION
+
+            directive @DirectiveName on FIELD_DEFINITION
+
+            type ArgThatChanges {
+              field1(id: String): String
+            }
+
+            enum EnumTypeThatLosesAValue {
+              VALUE1
+              VALUE2
+            }
+
+            interface Interface1
+            type TypeThatLoosesInterface1
+
+            type TypeInUnion1
+            type TypeInUnion2
+
+            union UnionTypeThatLosesAType = TypeInUnion1
+
+            interface TypeThatChangesType
+
+            interface TypeThatHasBreakingFieldChanges {
+              field2: Boolean
+            }
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.TYPE_REMOVED,
+                "Standard scalar Float was removed"
+                " because it is not referenced anymore.",
+            ),
+            (BreakingChangeType.TYPE_REMOVED, "TypeThatGetsRemoved was removed."),
+            (
+                BreakingChangeType.ARG_CHANGED_KIND,
+                "ArgThatChanges.field1 arg id has changed type from Float to String.",
+            ),
+            (
+                BreakingChangeType.VALUE_REMOVED_FROM_ENUM,
+                "VALUE0 was removed from enum type EnumTypeThatLosesAValue.",
+            ),
+            (
+                BreakingChangeType.IMPLEMENTED_INTERFACE_REMOVED,
+                "TypeThatLoosesInterface1 no longer implements interface Interface1.",
+            ),
+            (
+                BreakingChangeType.TYPE_REMOVED_FROM_UNION,
+                "TypeInUnion2 was removed from union type UnionTypeThatLosesAType.",
+            ),
+            (
+                BreakingChangeType.TYPE_CHANGED_KIND,
+                "TypeThatChangesType changed from an Object type to an"
+                " Interface type.",
+            ),
+            (
+                BreakingChangeType.FIELD_REMOVED,
+                "TypeThatHasBreakingFieldChanges.field1 was removed.",
+            ),
+            (
+                BreakingChangeType.FIELD_CHANGED_KIND,
+                "TypeThatHasBreakingFieldChanges.field2 changed type"
+                " from String to Boolean.",
+            ),
+            (
+                BreakingChangeType.DIRECTIVE_REMOVED,
+                "DirectiveThatIsRemoved was removed.",
+            ),
+            (
+                BreakingChangeType.DIRECTIVE_ARG_REMOVED,
+                "arg1 was removed from DirectiveThatRemovesArg.",
+            ),
+            (
+                BreakingChangeType.REQUIRED_DIRECTIVE_ARG_ADDED,
+                "A required arg arg1 on directive NonNullDirectiveAdded was added.",
+            ),
+            (
+                BreakingChangeType.DIRECTIVE_REPEATABLE_REMOVED,
+                "Repeatable flag was removed from DirectiveThatWasRepeatable.",
+            ),
+            (
+                BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
+                "QUERY was removed from DirectiveName.",
+            ),
+        ]
+
+    def should_detect_if_a_directive_was_explicitly_removed():
+        old_schema = build_schema(
+            """
+            directive @DirectiveThatIsRemoved on FIELD_DEFINITION
+            directive @DirectiveThatStays on FIELD_DEFINITION
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            directive @DirectiveThatStays on FIELD_DEFINITION
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.DIRECTIVE_REMOVED,
+                "DirectiveThatIsRemoved was removed.",
+            )
+        ]
+
+    def should_detect_if_a_directive_was_implicitly_removed():
+        old_schema = GraphQLSchema()
+
+        new_schema = GraphQLSchema(
+            directives=[
+                GraphQLSkipDirective,
+                GraphQLIncludeDirective,
+                GraphQLSpecifiedByDirective,
+            ]
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.DIRECTIVE_REMOVED,
+                f"{GraphQLDeprecatedDirective.name} was removed.",
+            )
+        ]
+
+    def should_detect_if_a_directive_argument_was_removed():
+        old_schema = build_schema(
+            """
+            directive @DirectiveWithArg(arg1: String) on FIELD_DEFINITION
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            directive @DirectiveWithArg on FIELD_DEFINITION
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.DIRECTIVE_ARG_REMOVED,
+                "arg1 was removed from DirectiveWithArg.",
+            )
+        ]
+
+    def should_detect_if_an_optional_directive_argument_was_added():
+        old_schema = build_schema(
+            """
+            directive @DirectiveName on FIELD_DEFINITION
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            directive @DirectiveName(
+              newRequiredArg: String!
+              newOptionalArg1: Int
+              newOptionalArg2: Int! = 0
+            ) on FIELD_DEFINITION
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.REQUIRED_DIRECTIVE_ARG_ADDED,
+                "A required arg newRequiredArg on directive DirectiveName was added.",
+            )
+        ]
+
+    def should_detect_removal_of_repeatable_flag():
+        old_schema = build_schema(
+            """
+            directive @DirectiveName repeatable on OBJECT
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            directive @DirectiveName on OBJECT
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.DIRECTIVE_REPEATABLE_REMOVED,
+                "Repeatable flag was removed from DirectiveName.",
+            )
+        ]
+
+    def should_detect_locations_removed_from_a_directive():
+        old_schema = build_schema(
+            """
+            directive @DirectiveName on FIELD_DEFINITION | QUERY
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            directive @DirectiveName on FIELD_DEFINITION
+            """
+        )
+
+        assert find_breaking_changes(old_schema, new_schema) == [
+            (
+                BreakingChangeType.DIRECTIVE_LOCATION_REMOVED,
+                "QUERY was removed from DirectiveName.",
+            )
+        ]
+
+
+def describe_find_dangerous_changes():
+    def should_detect_if_a_default_value_changed_on_an_argument():
+        old_sdl = """
+            input Input1 {
+              innerInputArray: [Input2]
+            }
+
+            input Input2 {
+              arrayField: [Int]
+            }
+
+            type Type1 {
+              field1(
+                withDefaultValue: String = "TO BE DELETED"
+                stringArg: String = "test"
+                emptyArray: [Int!] = []
+                valueArray: [[String]] = [["a", "b"], ["c"]]
+                complexObject: Input1 = {
+                  innerInputArray: [{ arrayField: [1, 2, 3] }]
+                }
+              ): String
+            }
+            """
+
+        old_schema = build_schema(old_sdl)
+        copy_of_old_schema = build_schema(old_sdl)
+        assert find_dangerous_changes(old_schema, copy_of_old_schema) == []
+
+        new_schema = build_schema(
+            """
+            input Input1 {
+              innerInputArray: [Input2]
+            }
+
+            input Input2 {
+              arrayField: [Int]
+            }
+
+            type Type1 {
+              field1(
+                withDefaultValue: String
+                stringArg: String = "Test"
+                emptyArray: [Int!] = [7]
+                valueArray: [[String]] = [["b", "a"], ["d"]]
+                complexObject: Input1 = {
+                  innerInputArray: [{ arrayField: [3, 2, 1] }]
+                }
+              ): String
+            }
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == [
+            (
+                DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
+                "Type1.field1 arg withDefaultValue defaultValue was removed.",
+            ),
+            (
+                DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
+                "Type1.field1 arg stringArg has changed defaultValue"
+                ' from "test" to "Test".',
+            ),
+            (
+                DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
+                "Type1.field1 arg emptyArray has changed defaultValue from [] to [7].",
+            ),
+            (
+                DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
+                "Type1.field1 arg valueArray has changed defaultValue"
+                ' from [["a", "b"], ["c"]] to [["b", "a"], ["d"]].',
+            ),
+            (
+                DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
+                "Type1.field1 arg complexObject has changed defaultValue"
+                " from {innerInputArray: [{arrayField: [1, 2, 3]}]}"
+                " to {innerInputArray: [{arrayField: [3, 2, 1]}]}.",
+            ),
+        ]
+
+    def should_ignore_changes_in_field_order_of_default_value():
+        old_schema = build_schema(
+            """
+            input Input1 {
+              a: String
+              b: String
+              c: String
+            }
+
+            type Type1 {
+              field1(
+                arg1: Input1 = { a: "a", b: "b", c: "c" }
+              ): String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+           input Input1 {
+             a: String
+             b: String
+             c: String
+           }
+
+           type Type1 {
+             field1(
+               arg1: Input1 = { c: "c", b: "b", a: "a" }
+             ): String
+           }
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == []
+
+    def should_ignore_changes_in_field_definitions_order():
+        old_schema = build_schema(
+            """
+            input Input1 {
+              a: String
+              b: String
+              c: String
+            }
+
+            type Type1 {
+              field1(
+                arg1: Input1 = { a: "a", b: "b", c: "c" }
+              ): String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            input Input1 {
+              c: String
+              b: String
+              a: String
+            }
+
+            type Type1 {
+              field1(
+                arg1: Input1 = { a: "a", b: "b", c: "c" }
+              ): String
+            }
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == []
+
+    def should_detect_if_a_value_was_added_to_an_enum_type():
+        old_schema = build_schema(
+            """
+            enum EnumType1 {
+              VALUE0
+              VALUE1
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            enum EnumType1 {
+              VALUE0
+              VALUE1
+              VALUE2
+            }
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == [
+            (
+                DangerousChangeType.VALUE_ADDED_TO_ENUM,
+                "VALUE2 was added to enum type EnumType1.",
+            )
+        ]
+
+    def should_detect_interfaces_added_to_types():
+        old_schema = build_schema(
+            """
+            interface OldInterface
+            interface NewInterface
+
+            type Type1 implements OldInterface
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            interface OldInterface
+            interface NewInterface
+
+            type Type1 implements OldInterface & NewInterface
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == [
+            (
+                DangerousChangeType.IMPLEMENTED_INTERFACE_ADDED,
+                "NewInterface added to interfaces implemented by Type1.",
+            )
+        ]
+
+    def should_detect_interfaces_added_to_interfaces():
+        old_schema = build_schema(
+            """
+            interface OldInterface
+            interface NewInterface
+
+            interface Interface1 implements OldInterface
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            interface OldInterface
+            interface NewInterface
+
+            interface Interface1 implements OldInterface & NewInterface
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == [
+            (
+                DangerousChangeType.IMPLEMENTED_INTERFACE_ADDED,
+                "NewInterface added to interfaces implemented by Interface1.",
+            )
+        ]
+
+    def should_detect_if_a_type_was_added_to_a_union_type():
+        old_schema = build_schema(
+            """
+            type Type1
+            type Type2
+
+            union UnionType1 = Type1
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type Type1
+            type Type2
+
+            union UnionType1 = Type1 | Type2
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == [
+            (
+                DangerousChangeType.TYPE_ADDED_TO_UNION,
+                "Type2 was added to union type UnionType1.",
+            )
+        ]
+
+    def should_detect_if_an_optional_field_was_added_to_an_input():
+        old_schema = build_schema(
+            """
+            input InputType1 {
+                field1: String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            input InputType1 {
+              field1: String
+              field2: Int
+            }
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == [
+            (
+                DangerousChangeType.OPTIONAL_INPUT_FIELD_ADDED,
+                "An optional field field2 on input type InputType1 was added.",
+            )
+        ]
+
+    def should_find_all_dangerous_changes():
+        old_schema = build_schema(
+            """
+            enum EnumType1 {
+              VALUE0
+              VALUE1
+            }
+
+            type Type1 {
+              field1(argThatChangesDefaultValue: String = "test"): String
+            }
+
+            interface Interface1
+            type TypeThatGainsInterface1
+
+            type TypeInUnion1
+            union UnionTypeThatGainsAType = TypeInUnion1
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            enum EnumType1 {
+              VALUE0
+              VALUE1
+              VALUE2
+            }
+
+            type Type1 {
+              field1(argThatChangesDefaultValue: String = "Test"): String
+            }
+
+            interface Interface1
+            type TypeThatGainsInterface1 implements Interface1
+
+            type TypeInUnion1
+            type TypeInUnion2
+            union UnionTypeThatGainsAType = TypeInUnion1 | TypeInUnion2
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == [
+            (
+                DangerousChangeType.VALUE_ADDED_TO_ENUM,
+                "VALUE2 was added to enum type EnumType1.",
+            ),
+            (
+                DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE,
+                "Type1.field1 arg argThatChangesDefaultValue has changed defaultValue"
+                ' from "test" to "Test".',
+            ),
+            (
+                DangerousChangeType.IMPLEMENTED_INTERFACE_ADDED,
+                "Interface1 added to interfaces implemented"
+                " by TypeThatGainsInterface1.",
+            ),
+            (
+                DangerousChangeType.TYPE_ADDED_TO_UNION,
+                "TypeInUnion2 was added to union type UnionTypeThatGainsAType.",
+            ),
+        ]
+
+    def should_detect_if_an_optional_field_argument_was_added():
+        old_schema = build_schema(
+            """
+            type Type1 {
+              field1(arg1: String): String
+            }
+            """
+        )
+
+        new_schema = build_schema(
+            """
+            type Type1 {
+              field1(arg1: String, arg2: String): String
+            }
+            """
+        )
+
+        assert find_dangerous_changes(old_schema, new_schema) == [
+            (
+                DangerousChangeType.OPTIONAL_ARG_ADDED,
+                "An optional arg arg2 on Type1.field1 was added.",
+            )
+        ]
diff --git a/tests/utilities/test_find_deprecated_usages.py b/tests/utilities/test_find_deprecated_usages.py
new file mode 100644
index 0000000..3e469cc
--- /dev/null
+++ b/tests/utilities/test_find_deprecated_usages.py
@@ -0,0 +1,71 @@
+from graphql.language import parse
+from graphql.utilities import build_schema, find_deprecated_usages
+
+
+def describe_find_deprecated_usages():
+
+    schema = build_schema(
+        """
+        enum EnumType {
+          NORMAL_VALUE
+          DEPRECATED_VALUE @deprecated(reason: "Some enum reason.")
+        }
+
+        type Query {
+          normalField(enumArg: EnumType): String
+          deprecatedField: String @deprecated(reason: "Some field reason.")
+        }
+        """
+    )
+
+    def should_report_empty_set_for_no_deprecated_usages():
+        errors = find_deprecated_usages(
+            schema, parse("{ normalField(enumArg: [NORMAL_VALUE]) }")
+        )
+
+        assert errors == []
+
+    def should_ignore_unknown_stuff():
+        errors = find_deprecated_usages(
+            schema,
+            parse(
+                """
+                {
+                  unknownField(unknownArg: UNKNOWN_VALUE)
+                  normalField(enumArg: UNKNOWN_VALUE)
+                }
+                """
+            ),
+        )
+
+        assert errors == []
+
+    def should_report_usage_of_deprecated_fields():
+        errors = find_deprecated_usages(
+            schema, parse("{ normalField, deprecatedField }")
+        )
+
+        error_messages = [err.message for err in errors]
+
+        assert error_messages == [
+            "The field 'Query.deprecatedField' is deprecated. Some field reason."
+        ]
+
+    def should_report_usage_of_deprecated_enums():
+        errors = find_deprecated_usages(
+            schema,
+            parse(
+                """
+                {
+                  normalField(enumArg: [NORMAL_VALUE, DEPRECATED_VALUE])
+                }
+                """
+            ),
+        )
+
+        error_messages = [err.message for err in errors]
+
+        assert error_messages == [
+            "The enum value 'EnumType.DEPRECATED_VALUE' is deprecated."
+            " Some enum reason."
+        ]
diff --git a/tests/utilities/test_get_introspection_query.py b/tests/utilities/test_get_introspection_query.py
new file mode 100644
index 0000000..60bf570
--- /dev/null
+++ b/tests/utilities/test_get_introspection_query.py
@@ -0,0 +1,57 @@
+import re
+
+from graphql.utilities import get_introspection_query
+
+
+def describe_get_introspection_query():
+    def skips_all_description_fields():
+        has_descriptions = re.compile(r"\bdescription\b").search
+
+        assert has_descriptions(get_introspection_query())
+
+        assert has_descriptions(get_introspection_query(descriptions=True))
+
+        assert not has_descriptions(get_introspection_query(descriptions=False))
+
+    def includes_is_repeatable_field_on_directives():
+        has_repeatability = re.compile(r"\bisRepeatable\b").search
+
+        assert not has_repeatability(get_introspection_query())
+
+        assert has_repeatability(get_introspection_query(directive_is_repeatable=True))
+
+        assert not has_repeatability(
+            get_introspection_query(directive_is_repeatable=False)
+        )
+
+    def includes_description_field_on_schema():
+        all_descriptions = re.compile(r"\bdescription\b").findall
+
+        assert len(all_descriptions(get_introspection_query())) == 5
+
+        assert (
+            len(all_descriptions(get_introspection_query(schema_description=False)))
+            == 5
+        )
+
+        assert (
+            len(all_descriptions(get_introspection_query(schema_description=True))) == 6
+        )
+
+        assert not all_descriptions(
+            get_introspection_query(descriptions=False, schema_description=True)
+        )
+
+    def includes_specified_by_url_field():
+        all_specified_by_urls = re.compile(r"\bspecifiedByUrl\b").findall
+
+        assert not all_specified_by_urls(get_introspection_query())
+
+        assert not all_specified_by_urls(
+            get_introspection_query(specified_by_url=False)
+        )
+
+        assert (
+            len(all_specified_by_urls(get_introspection_query(specified_by_url=True)))
+            == 1
+        )
diff --git a/tests/utilities/test_get_operation_ast.py b/tests/utilities/test_get_operation_ast.py
new file mode 100644
index 0000000..c60ca1a
--- /dev/null
+++ b/tests/utilities/test_get_operation_ast.py
@@ -0,0 +1,62 @@
+from graphql.language import parse
+from graphql.utilities import get_operation_ast
+
+
+def describe_get_operation_ast():
+    def gets_an_operation_from_a_simple_document():
+        doc = parse("{ field }")
+        assert get_operation_ast(doc) == doc.definitions[0]
+
+    def gets_an_operation_from_a_document_with_named_op_mutation():
+        doc = parse("mutation Test { field }")
+        assert get_operation_ast(doc) == doc.definitions[0]
+
+    def gets_an_operation_from_a_document_with_named_op_subscription():
+        doc = parse("subscription Test { field }")
+        assert get_operation_ast(doc) == doc.definitions[0]
+
+    def does_not_get_missing_operation():
+        doc = parse("type Foo { field: String }")
+        assert get_operation_ast(doc) is None
+
+    def does_not_get_ambiguous_unnamed_operation():
+        doc = parse(
+            """
+            { field }
+            mutation Test { field }
+            subscription TestSub { field }
+            """
+        )
+        assert get_operation_ast(doc) is None
+
+    def does_not_get_ambiguous_named_operation():
+        doc = parse(
+            """
+            query TestQ { field }
+            mutation TestM { field }
+            subscription TestS { field }
+            """
+        )
+        assert get_operation_ast(doc) is None
+
+    def does_not_get_misnamed_operation():
+        doc = parse(
+            """
+            query TestQ { field }
+            mutation TestM { field }
+            subscription TestS { field }
+            """
+        )
+        assert get_operation_ast(doc, "Unknown") is None
+
+    def gets_named_operation():
+        doc = parse(
+            """
+            query TestQ { field }
+            mutation TestM { field }
+            subscription TestS { field }
+            """
+        )
+        assert get_operation_ast(doc, "TestQ") == doc.definitions[0]
+        assert get_operation_ast(doc, "TestM") == doc.definitions[1]
+        assert get_operation_ast(doc, "TestS") == doc.definitions[2]
diff --git a/tests/utilities/test_get_operation_root_type.py b/tests/utilities/test_get_operation_root_type.py
new file mode 100644
index 0000000..75343d6
--- /dev/null
+++ b/tests/utilities/test_get_operation_root_type.py
@@ -0,0 +1,114 @@
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.language import (
+    parse,
+    DocumentNode,
+    OperationDefinitionNode,
+    OperationTypeDefinitionNode,
+    SchemaDefinitionNode,
+)
+from graphql.type import GraphQLField, GraphQLObjectType, GraphQLSchema, GraphQLString
+from graphql.utilities import get_operation_root_type
+
+
+query_type = GraphQLObjectType("FooQuery", {"field": GraphQLField(GraphQLString)})
+
+mutation_type = GraphQLObjectType("FooMutation", {"field": GraphQLField(GraphQLString)})
+
+subscription_type = GraphQLObjectType(
+    "FooSubscription", {"field": GraphQLField(GraphQLString)}
+)
+
+
+def get_operation_node(doc: DocumentNode) -> OperationDefinitionNode:
+    operation_node = doc.definitions[0]
+    assert isinstance(operation_node, OperationDefinitionNode)
+    return operation_node
+
+
+def describe_get_operation_root_type():
+    def gets_a_query_type_for_an_unnamed_operation_definition_node():
+        test_schema = GraphQLSchema(query_type)
+        doc = parse("{ field }")
+        operation_node = get_operation_node(doc)
+        assert get_operation_root_type(test_schema, operation_node) is query_type
+
+    def gets_a_query_type_for_a_named_operation_definition_node():
+        test_schema = GraphQLSchema(query_type)
+        doc = parse("query Q { field }")
+        operation_node = get_operation_node(doc)
+        assert get_operation_root_type(test_schema, operation_node) is query_type
+
+    def gets_a_type_for_operation_definition_nodes():
+        test_schema = GraphQLSchema(query_type, mutation_type, subscription_type)
+        doc = parse(
+            """
+            schema {
+              query: FooQuery
+              mutation: FooMutation
+              subscription: FooSubscription
+            }
+            """
+        )
+
+        schema_node = doc.definitions[0]
+        assert isinstance(schema_node, SchemaDefinitionNode)
+        query_node, mutation_node, subscription_node = schema_node.operation_types
+        assert isinstance(query_node, OperationTypeDefinitionNode)
+        assert get_operation_root_type(test_schema, query_node) is query_type
+        assert isinstance(mutation_node, OperationTypeDefinitionNode)
+        assert get_operation_root_type(test_schema, mutation_node) is mutation_type
+        assert isinstance(subscription_node, OperationTypeDefinitionNode)
+        assert (
+            get_operation_root_type(test_schema, subscription_node) is subscription_type
+        )
+
+    def gets_a_mutation_type_for_an_operation_definition_node():
+        test_schema = GraphQLSchema(mutation=mutation_type)
+        doc = parse("mutation { field }")
+        operation_node = get_operation_node(doc)
+        assert get_operation_root_type(test_schema, operation_node) is mutation_type
+
+    def gets_a_subscription_type_for_an_operation_definition_node():
+        test_schema = GraphQLSchema(subscription=subscription_type)
+        doc = parse("subscription { field }")
+        operation_node = get_operation_node(doc)
+        assert get_operation_root_type(test_schema, operation_node) is subscription_type
+
+    def throws_when_query_type_not_defined_in_schema():
+        test_schema = GraphQLSchema()
+        doc = parse("query { field }")
+        operation_node = get_operation_node(doc)
+        with raises(GraphQLError) as exc_info:
+            get_operation_root_type(test_schema, operation_node)
+        assert exc_info.value.message == (
+            "Schema does not define the required query root type."
+        )
+
+    def throws_when_mutation_type_not_defined_in_schema():
+        test_schema = GraphQLSchema()
+        doc = parse("mutation { field }")
+        operation_node = get_operation_node(doc)
+        with raises(GraphQLError) as exc_info:
+            get_operation_root_type(test_schema, operation_node)
+        assert exc_info.value.message == "Schema is not configured for mutations."
+
+    def throws_when_subscription_type_not_defined_in_schema():
+        test_schema = GraphQLSchema()
+        doc = parse("subscription { field }")
+        operation_node = get_operation_node(doc)
+        with raises(GraphQLError) as exc_info:
+            get_operation_root_type(test_schema, operation_node)
+        assert exc_info.value.message == "Schema is not configured for subscriptions."
+
+    def throws_when_operation_not_a_valid_operation_kind():
+        test_schema = GraphQLSchema()
+        doc = parse("{ field }")
+        operation_node = get_operation_node(doc)
+        operation_node.operation = "non_existent_operation"  # type: ignore
+        with raises(GraphQLError) as exc_info:
+            get_operation_root_type(test_schema, operation_node)
+        assert exc_info.value.message == (
+            "Can only have query, mutation and subscription operations."
+        )
diff --git a/tests/utilities/test_introspection_from_schema.py b/tests/utilities/test_introspection_from_schema.py
new file mode 100644
index 0000000..bb510d3
--- /dev/null
+++ b/tests/utilities/test_introspection_from_schema.py
@@ -0,0 +1,59 @@
+from graphql.type import GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString
+from graphql.utilities import (
+    build_client_schema,
+    print_schema,
+    introspection_from_schema,
+)
+
+from ..utils import dedent
+
+
+def introspection_to_sdl(introspection):
+    return print_schema(build_client_schema(introspection))
+
+
+def describe_introspection_from_schema():
+
+    schema = GraphQLSchema(
+        GraphQLObjectType(
+            "Simple",
+            {
+                "string": GraphQLField(
+                    GraphQLString, description="This is a string field"
+                )
+            },
+            description="This is a simple type",
+        )
+    )
+
+    def converts_a_simple_schema():
+        introspection = introspection_from_schema(schema)
+
+        assert introspection_to_sdl(introspection) == dedent(
+            '''
+            schema {
+              query: Simple
+            }
+
+            """This is a simple type"""
+            type Simple {
+              """This is a string field"""
+              string: String
+            }
+            '''
+        )
+
+    def converts_a_simple_schema_without_description():
+        introspection = introspection_from_schema(schema, descriptions=False)
+
+        assert introspection_to_sdl(introspection) == dedent(
+            """
+            schema {
+              query: Simple
+            }
+
+            type Simple {
+              string: String
+            }
+            """
+        )
diff --git a/tests/utilities/test_lexicographic_sort_schema.py b/tests/utilities/test_lexicographic_sort_schema.py
new file mode 100644
index 0000000..4366448
--- /dev/null
+++ b/tests/utilities/test_lexicographic_sort_schema.py
@@ -0,0 +1,384 @@
+from graphql.utilities import build_schema, print_schema, lexicographic_sort_schema
+
+from ..utils import dedent
+
+
+def sort_sdl(sdl):
+    schema = build_schema(sdl)
+    return print_schema(lexicographic_sort_schema(schema))
+
+
+def describe_lexicographic_sort_schema():
+    def sort_fields():
+        sorted_sdl = sort_sdl(
+            """
+            input Bar {
+              barB: String!
+              barA: String
+              barC: [String]
+            }
+
+            interface FooInterface {
+              fooB: String!
+              fooA: String
+              fooC: [String]
+            }
+
+            type FooType implements FooInterface {
+              fooC: [String]
+              fooA: String
+              fooB: String!
+            }
+
+            type Query {
+              dummy(arg: Bar): FooType
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            input Bar {
+              barA: String
+              barB: String!
+              barC: [String]
+            }
+
+            interface FooInterface {
+              fooA: String
+              fooB: String!
+              fooC: [String]
+            }
+
+            type FooType implements FooInterface {
+              fooA: String
+              fooB: String!
+              fooC: [String]
+            }
+
+            type Query {
+              dummy(arg: Bar): FooType
+            }
+            """
+        )
+
+    def sort_implemented_interfaces():
+        sorted_sdl = sort_sdl(
+            """
+            interface FooA {
+              dummy: String
+            }
+
+            interface FooB {
+              dummy: String
+            }
+
+            interface FooC implements FooB & FooA {
+              dummy: String
+            }
+
+            type Query implements FooB & FooA & FooC {
+              dummy: String
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            interface FooA {
+              dummy: String
+            }
+
+            interface FooB {
+              dummy: String
+            }
+
+            interface FooC implements FooA & FooB {
+              dummy: String
+            }
+
+            type Query implements FooA & FooB & FooC {
+              dummy: String
+            }
+            """
+        )
+
+    def sort_types_in_union():
+        sorted_sdl = sort_sdl(
+            """
+            type FooA {
+              dummy: String
+            }
+
+            type FooB {
+              dummy: String
+            }
+
+            type FooC {
+              dummy: String
+            }
+
+            union FooUnion = FooB | FooA | FooC
+
+            type Query {
+              dummy: FooUnion
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            type FooA {
+              dummy: String
+            }
+
+            type FooB {
+              dummy: String
+            }
+
+            type FooC {
+              dummy: String
+            }
+
+            union FooUnion = FooA | FooB | FooC
+
+            type Query {
+              dummy: FooUnion
+            }
+            """
+        )
+
+    def sort_enum_types():
+        sorted_sdl = sort_sdl(
+            """
+            enum Foo {
+              B
+              C
+              A
+            }
+
+            type Query {
+              dummy: Foo
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            enum Foo {
+              A
+              B
+              C
+            }
+
+            type Query {
+              dummy: Foo
+            }
+            """
+        )
+
+    def sort_field_arguments():
+        sorted_sdl = sort_sdl(
+            """
+            type Query {
+              dummy(argB: Int!, argA: String, argC: [Float]): ID
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            type Query {
+              dummy(argA: String, argB: Int!, argC: [Float]): ID
+            }
+            """
+        )
+
+    def sort_types():
+        sorted_sdl = sort_sdl(
+            """
+            type Query {
+              dummy(arg1: FooF, arg2: FooA, arg3: FooG): FooD
+            }
+
+            type FooC implements FooE {
+              dummy: String
+            }
+
+            enum FooG {
+              enumValue
+            }
+
+            scalar FooA
+
+            input FooF {
+              dummy: String
+            }
+
+            union FooD = FooC | FooB
+
+            interface FooE {
+              dummy: String
+            }
+
+            type FooB {
+              dummy: String
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            scalar FooA
+
+            type FooB {
+              dummy: String
+            }
+
+            type FooC implements FooE {
+              dummy: String
+            }
+
+            union FooD = FooB | FooC
+
+            interface FooE {
+              dummy: String
+            }
+
+            input FooF {
+              dummy: String
+            }
+
+            enum FooG {
+              enumValue
+            }
+
+            type Query {
+              dummy(arg1: FooF, arg2: FooA, arg3: FooG): FooD
+            }
+            """
+        )
+
+    def sort_directive_arguments():
+        sorted_sdl = sort_sdl(
+            """
+            directive @test(argC: Float, argA: String, argB: Int) on FIELD
+
+            type Query {
+              dummy: String
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            directive @test(argA: String, argB: Int, argC: Float) on FIELD
+
+            type Query {
+              dummy: String
+            }
+            """
+        )
+
+    def sort_directive_locations():
+        sorted_sdl = sort_sdl(
+            """
+            directive @test(argC: Float, argA: String, argB: Int) on UNION | FIELD | ENUM
+
+            type Query {
+              dummy: String
+            }
+            """  # noqa: E501
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            directive @test(argA: String, argB: Int, argC: Float) on ENUM | FIELD | UNION
+
+            type Query {
+              dummy: String
+            }
+            """  # noqa: E501
+        )
+
+    def sort_directives():
+        sorted_sdl = sort_sdl(
+            """
+            directive @fooC on FIELD
+
+            directive @fooB on UNION
+
+            directive @fooA on ENUM
+
+            type Query {
+              dummy: String
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            directive @fooA on ENUM
+
+            directive @fooB on UNION
+
+            directive @fooC on FIELD
+
+            type Query {
+              dummy: String
+            }
+            """
+        )
+
+    def sort_recursive_types():
+        sorted_sdl = sort_sdl(
+            """
+            interface FooC {
+              fooB: FooB
+              fooA: FooA
+              fooC: FooC
+            }
+
+            type FooB implements FooC {
+              fooB: FooB
+              fooA: FooA
+            }
+
+            type FooA implements FooC {
+              fooB: FooB
+              fooA: FooA
+            }
+
+            type Query {
+              fooC: FooC
+              fooB: FooB
+              fooA: FooA
+            }
+            """
+        )
+
+        assert sorted_sdl == dedent(
+            """
+            type FooA implements FooC {
+              fooA: FooA
+              fooB: FooB
+            }
+
+            type FooB implements FooC {
+              fooA: FooA
+              fooB: FooB
+            }
+
+            interface FooC {
+              fooA: FooA
+              fooB: FooB
+              fooC: FooC
+            }
+
+            type Query {
+              fooA: FooA
+              fooB: FooB
+              fooC: FooC
+            }
+            """
+        )
diff --git a/tests/utilities/test_print_schema.py b/tests/utilities/test_print_schema.py
new file mode 100644
index 0000000..f7a79b2
--- /dev/null
+++ b/tests/utilities/test_print_schema.py
@@ -0,0 +1,841 @@
+from typing import cast, Any, Dict
+
+from graphql.language import DirectiveLocation
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLBoolean,
+    GraphQLEnumType,
+    GraphQLField,
+    GraphQLFloat,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLScalarType,
+    GraphQLSchema,
+    GraphQLString,
+    GraphQLUnionType,
+    GraphQLInputField,
+    GraphQLDirective,
+)
+from graphql.utilities import (
+    build_schema,
+    print_schema,
+    print_introspection_schema,
+    print_value,
+)
+
+from ..utils import dedent
+
+
+def expect_printed_schema(schema: GraphQLSchema) -> str:
+    schema_text = print_schema(schema)
+    # keep print_schema and build_schema in sync
+    assert print_schema(build_schema(schema_text)) == schema_text
+    return schema_text
+
+
+def build_single_field_schema(field: GraphQLField):
+    query = GraphQLObjectType(name="Query", fields={"singleField": field})
+    return GraphQLSchema(query=query)
+
+
+def describe_type_system_printer():
+    def prints_string_field():
+        schema = build_single_field_schema(GraphQLField(GraphQLString))
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField: String
+            }
+            """
+        )
+
+    def prints_list_of_string_field():
+        schema = build_single_field_schema(GraphQLField(GraphQLList(GraphQLString)))
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField: [String]
+            }
+            """
+        )
+
+    def prints_non_null_string_field():
+        schema = build_single_field_schema(GraphQLField(GraphQLNonNull(GraphQLString)))
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField: String!
+            }
+            """
+        )
+
+    def prints_non_null_list_of_string_field():
+        schema = build_single_field_schema(
+            GraphQLField(GraphQLNonNull(GraphQLList(GraphQLString)))
+        )
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField: [String]!
+            }
+            """
+        )
+
+    def prints_list_of_non_null_string_field():
+        schema = build_single_field_schema(
+            GraphQLField((GraphQLList(GraphQLNonNull(GraphQLString))))
+        )
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField: [String!]
+            }
+            """
+        )
+
+    def prints_non_null_list_of_non_null_string_field():
+        schema = build_single_field_schema(
+            GraphQLField(GraphQLNonNull(GraphQLList(GraphQLNonNull(GraphQLString))))
+        )
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField: [String!]!
+            }
+            """
+        )
+
+    def prints_object_field():
+        foo_type = GraphQLObjectType(
+            name="Foo", fields={"str": GraphQLField(GraphQLString)}
+        )
+        schema = GraphQLSchema(types=[foo_type])
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Foo {
+              str: String
+            }
+            """
+        )
+
+    def prints_string_field_with_int_arg():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString, args={"argOne": GraphQLArgument(GraphQLInt)}
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField(argOne: Int): String
+            }
+            """
+        )
+
+    def prints_string_field_with_int_arg_with_default():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString,
+                args={"argOne": GraphQLArgument(GraphQLInt, default_value=2)},
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField(argOne: Int = 2): String
+            }
+            """
+        )
+
+    def prints_string_field_with_string_arg_with_default():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString,
+                args={
+                    "argOne": GraphQLArgument(
+                        GraphQLString, default_value="tes\t de\fault"
+                    )
+                },
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            r"""
+            type Query {
+              singleField(argOne: String = "tes\t de\fault"): String
+            }
+            """
+        )
+
+    def prints_string_field_with_int_arg_with_default_null():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString,
+                args={"argOne": GraphQLArgument(GraphQLInt, default_value=None)},
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField(argOne: Int = null): String
+            }
+            """
+        )
+
+    def prints_string_field_with_non_null_int_arg():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString,
+                args={"argOne": GraphQLArgument(GraphQLNonNull(GraphQLInt))},
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField(argOne: Int!): String
+            }
+            """
+        )
+
+    def prints_string_field_with_multiple_args():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString,
+                args={
+                    "argOne": GraphQLArgument(GraphQLInt),
+                    "argTwo": GraphQLArgument(GraphQLString),
+                },
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField(argOne: Int, argTwo: String): String
+            }
+            """
+        )
+
+    def prints_string_field_with_multiple_args_first_is_default():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString,
+                args={
+                    "argOne": GraphQLArgument(GraphQLInt, default_value=1),
+                    "argTwo": GraphQLArgument(GraphQLString),
+                    "argThree": GraphQLArgument(GraphQLBoolean),
+                },
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField(argOne: Int = 1, argTwo: String, argThree: Boolean): String
+            }
+            """
+        )
+
+    def prints_string_field_with_multiple_args_second_is_default():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString,
+                args={
+                    "argOne": GraphQLArgument(GraphQLInt),
+                    "argTwo": GraphQLArgument(GraphQLString, default_value="foo"),
+                    "argThree": GraphQLArgument(GraphQLBoolean),
+                },
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField(argOne: Int, argTwo: String = "foo", argThree: Boolean): String
+            }
+            """  # noqa: E501
+        )
+
+    def prints_string_field_with_multiple_args_last_is_default():
+        schema = build_single_field_schema(
+            GraphQLField(
+                type_=GraphQLString,
+                args={
+                    "argOne": GraphQLArgument(GraphQLInt),
+                    "argTwo": GraphQLArgument(GraphQLString),
+                    "argThree": GraphQLArgument(GraphQLBoolean, default_value=False),
+                },
+            )
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Query {
+              singleField(argOne: Int, argTwo: String, argThree: Boolean = false): String
+            }
+            """  # noqa: E501
+        )
+
+    def prints_schema_with_description():
+        schema = GraphQLSchema(
+            description="Schema description.", query=GraphQLObjectType("Query", {})
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            '''
+            """Schema description."""
+            schema {
+              query: Query
+            }
+
+            type Query
+            '''
+        )
+
+    def prints_custom_query_root_types():
+        schema = GraphQLSchema(query=GraphQLObjectType("CustomType", {}))
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            schema {
+              query: CustomType
+            }
+
+            type CustomType
+            """
+        )
+
+    def prints_custom_mutation_root_types():
+        schema = GraphQLSchema(mutation=GraphQLObjectType("CustomType", {}))
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            schema {
+              mutation: CustomType
+            }
+
+            type CustomType
+            """
+        )
+
+    def prints_custom_subscription_root_types():
+        schema = GraphQLSchema(subscription=GraphQLObjectType("CustomType", {}))
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            schema {
+              subscription: CustomType
+            }
+
+            type CustomType
+            """
+        )
+
+    def prints_interface():
+        foo_type = GraphQLInterfaceType(
+            name="Foo", fields={"str": GraphQLField(GraphQLString)}
+        )
+
+        bar_type = GraphQLObjectType(
+            name="Bar",
+            fields={"str": GraphQLField(GraphQLString)},
+            interfaces=[foo_type],
+        )
+
+        schema = GraphQLSchema(types=[bar_type])
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Bar implements Foo {
+              str: String
+            }
+
+            interface Foo {
+              str: String
+            }
+            """
+        )
+
+    def prints_multiple_interfaces():
+        foo_type = GraphQLInterfaceType(
+            name="Foo", fields={"str": GraphQLField(GraphQLString)}
+        )
+
+        baz_type = GraphQLInterfaceType(
+            name="Baz", fields={"int": GraphQLField(GraphQLInt)}
+        )
+
+        bar_type = GraphQLObjectType(
+            name="Bar",
+            fields={
+                "str": GraphQLField(GraphQLString),
+                "int": GraphQLField(GraphQLInt),
+            },
+            interfaces=[foo_type, baz_type],
+        )
+
+        schema = GraphQLSchema(types=[bar_type])
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Bar implements Foo & Baz {
+              str: String
+              int: Int
+            }
+
+            interface Foo {
+              str: String
+            }
+
+            interface Baz {
+              int: Int
+            }
+            """
+        )
+
+    def prints_hierarchical_interface():
+        foo_type = GraphQLInterfaceType(
+            name="Foo", fields={"str": GraphQLField(GraphQLString)}
+        )
+
+        baz_type = GraphQLInterfaceType(
+            name="Baz",
+            interfaces=[foo_type],
+            fields={
+                "int": GraphQLField(GraphQLInt),
+                "str": GraphQLField(GraphQLString),
+            },
+        )
+
+        bar_type = GraphQLObjectType(
+            name="Bar",
+            fields={
+                "str": GraphQLField(GraphQLString),
+                "int": GraphQLField(GraphQLInt),
+            },
+            interfaces=[foo_type, baz_type],
+        )
+
+        query = GraphQLObjectType(name="Query", fields={"bar": GraphQLField(bar_type)})
+
+        schema = GraphQLSchema(query, types=[bar_type])
+        assert expect_printed_schema(schema) == dedent(
+            """
+            type Bar implements Foo & Baz {
+              str: String
+              int: Int
+            }
+
+            interface Foo {
+              str: String
+            }
+
+            interface Baz implements Foo {
+              int: Int
+              str: String
+            }
+
+            type Query {
+              bar: Bar
+            }
+            """
+        )
+
+    def prints_unions():
+        foo_type = GraphQLObjectType(
+            name="Foo", fields={"bool": GraphQLField(GraphQLBoolean)}
+        )
+
+        bar_type = GraphQLObjectType(
+            name="Bar", fields={"str": GraphQLField(GraphQLString)}
+        )
+
+        single_union = GraphQLUnionType(name="SingleUnion", types=[foo_type])
+
+        multiple_union = GraphQLUnionType(
+            name="MultipleUnion", types=[foo_type, bar_type]
+        )
+
+        schema = GraphQLSchema(types=[single_union, multiple_union])
+        assert expect_printed_schema(schema) == dedent(
+            """
+            union SingleUnion = Foo
+
+            type Foo {
+              bool: Boolean
+            }
+
+            union MultipleUnion = Foo | Bar
+
+            type Bar {
+              str: String
+            }
+            """
+        )
+
+    def prints_input_type():
+        input_type = GraphQLInputObjectType(
+            name="InputType", fields={"int": GraphQLInputField(GraphQLInt)}
+        )
+
+        schema = GraphQLSchema(types=[input_type])
+        assert expect_printed_schema(schema) == dedent(
+            """
+            input InputType {
+              int: Int
+            }
+            """
+        )
+
+    def prints_custom_scalar():
+        odd_type = GraphQLScalarType(name="Odd")
+
+        schema = GraphQLSchema(types=[odd_type])
+        assert expect_printed_schema(schema) == dedent(
+            """
+            scalar Odd
+            """
+        )
+
+    def prints_custom_scalar_with_speicified_by_url():
+        foo_type = GraphQLScalarType(
+            name="Foo", specified_by_url="https://example.com/foo_spec"
+        )
+
+        schema = GraphQLSchema(types=[foo_type])
+        assert expect_printed_schema(schema) == dedent(
+            """
+            scalar Foo @specifiedBy(url: "https://example.com/foo_spec")
+            """
+        )
+
+    def prints_enum():
+        rgb_type = GraphQLEnumType(
+            name="RGB", values=dict.fromkeys(("RED", "GREEN", "BLUE"))
+        )
+
+        schema = GraphQLSchema(types=[rgb_type])
+        assert expect_printed_schema(schema) == dedent(
+            """
+            enum RGB {
+              RED
+              GREEN
+              BLUE
+            }
+            """
+        )
+
+    def prints_empty_types():
+        schema = GraphQLSchema(
+            types=[
+                GraphQLEnumType("SomeEnum", cast(Dict[str, Any], {})),
+                GraphQLInputObjectType("SomeInputObject", {}),
+                GraphQLInterfaceType("SomeInterface", {}),
+                GraphQLObjectType("SomeObject", {}),
+                GraphQLUnionType("SomeUnion", []),
+            ]
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            """
+            enum SomeEnum
+
+            input SomeInputObject
+
+            interface SomeInterface
+
+            type SomeObject
+
+            union SomeUnion
+            """
+        )
+
+    def prints_custom_directives():
+        simple_directive = GraphQLDirective(
+            "simpleDirective", [DirectiveLocation.FIELD]
+        )
+        complex_directive = GraphQLDirective(
+            "complexDirective",
+            [DirectiveLocation.FIELD, DirectiveLocation.QUERY],
+            description="Complex Directive",
+            args={
+                "stringArg": GraphQLArgument(GraphQLString),
+                "intArg": GraphQLArgument(GraphQLInt, default_value=-1),
+            },
+            is_repeatable=True,
+        )
+
+        schema = GraphQLSchema(directives=[simple_directive, complex_directive])
+        assert expect_printed_schema(schema) == dedent(
+            '''
+            directive @simpleDirective on FIELD
+
+            """Complex Directive"""
+            directive @complexDirective(stringArg: String, intArg: Int = -1) repeatable on FIELD | QUERY
+            '''  # noqa: E501
+        )
+
+    def prints_an_empty_description():
+        schema = build_single_field_schema(GraphQLField(GraphQLString, description=""))
+
+        assert expect_printed_schema(schema) == dedent(
+            '''
+            type Query {
+              """"""
+              singleField: String
+            }
+            '''
+        )
+
+    def one_line_prints_a_short_description():
+        schema = build_single_field_schema(
+            GraphQLField(GraphQLString, description="This field is awesome")
+        )
+
+        assert expect_printed_schema(schema) == dedent(
+            '''
+            type Query {
+              """This field is awesome"""
+              singleField: String
+            }
+            '''
+        )
+
+    def prints_introspection_schema():
+        schema = GraphQLSchema()
+        output = print_introspection_schema(schema)
+
+        assert output == dedent(
+            '''
+            """
+            Directs the executor to include this field or fragment only when the `if` argument is true.
+            """
+            directive @include(
+              """Included when true."""
+              if: Boolean!
+            ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+            """
+            Directs the executor to skip this field or fragment when the `if` argument is true.
+            """
+            directive @skip(
+              """Skipped when true."""
+              if: Boolean!
+            ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
+
+            """Marks an element of a GraphQL schema as no longer supported."""
+            directive @deprecated(
+              """
+              Explains why this element was deprecated, usually also including a suggestion for how to access supported similar data. Formatted using the Markdown syntax, as specified by [CommonMark](https://commonmark.org/).
+              """
+              reason: String = "No longer supported"
+            ) on FIELD_DEFINITION | ENUM_VALUE
+
+            """Exposes a URL that specifies the behaviour of this scalar."""
+            directive @specifiedBy(
+              """The URL that specifies the behaviour of this scalar."""
+              url: String!
+            ) on SCALAR
+
+            """
+            A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all available types and directives on the server, as well as the entry points for query, mutation, and subscription operations.
+            """
+            type __Schema {
+              description: String
+
+              """A list of all types supported by this server."""
+              types: [__Type!]!
+
+              """The type that query operations will be rooted at."""
+              queryType: __Type!
+
+              """
+              If this server supports mutation, the type that mutation operations will be rooted at.
+              """
+              mutationType: __Type
+
+              """
+              If this server support subscription, the type that subscription operations will be rooted at.
+              """
+              subscriptionType: __Type
+
+              """A list of all directives supported by this server."""
+              directives: [__Directive!]!
+            }
+
+            """
+            The fundamental unit of any GraphQL Schema is the type. There are many kinds of types in GraphQL as represented by the `__TypeKind` enum.
+
+            Depending on the kind of a type, certain fields describe information about that type. Scalar types provide no information beyond a name, description and optional `specifiedByUrl`, while Enum types provide their values. Object and Interface types provide the fields they describe. Abstract types, Union and Interface, provide the Object types possible at runtime. List and NonNull types compose other types.
+            """
+            type __Type {
+              kind: __TypeKind!
+              name: String
+              description: String
+              specifiedByUrl: String
+              fields(includeDeprecated: Boolean = false): [__Field!]
+              interfaces: [__Type!]
+              possibleTypes: [__Type!]
+              enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
+              inputFields: [__InputValue!]
+              ofType: __Type
+            }
+
+            """An enum describing what kind of type a given `__Type` is."""
+            enum __TypeKind {
+              """Indicates this type is a scalar."""
+              SCALAR
+
+              """
+              Indicates this type is an object. `fields` and `interfaces` are valid fields.
+              """
+              OBJECT
+
+              """
+              Indicates this type is an interface. `fields`, `interfaces`, and `possibleTypes` are valid fields.
+              """
+              INTERFACE
+
+              """Indicates this type is a union. `possibleTypes` is a valid field."""
+              UNION
+
+              """Indicates this type is an enum. `enumValues` is a valid field."""
+              ENUM
+
+              """
+              Indicates this type is an input object. `inputFields` is a valid field.
+              """
+              INPUT_OBJECT
+
+              """Indicates this type is a list. `ofType` is a valid field."""
+              LIST
+
+              """Indicates this type is a non-null. `ofType` is a valid field."""
+              NON_NULL
+            }
+
+            """
+            Object and Interface types are described by a list of Fields, each of which has a name, potentially a list of arguments, and a return type.
+            """
+            type __Field {
+              name: String!
+              description: String
+              args: [__InputValue!]!
+              type: __Type!
+              isDeprecated: Boolean!
+              deprecationReason: String
+            }
+
+            """
+            Arguments provided to Fields or Directives and the input fields of an InputObject are represented as Input Values which describe their type and optionally a default value.
+            """
+            type __InputValue {
+              name: String!
+              description: String
+              type: __Type!
+
+              """
+              A GraphQL-formatted string representing the default value for this input value.
+              """
+              defaultValue: String
+            }
+
+            """
+            One possible value for a given Enum. Enum values are unique values, not a placeholder for a string or numeric value. However an Enum value is returned in a JSON response as a string.
+            """
+            type __EnumValue {
+              name: String!
+              description: String
+              isDeprecated: Boolean!
+              deprecationReason: String
+            }
+
+            """
+            A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
+
+            In some cases, you need to provide options to alter GraphQL's execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.
+            """
+            type __Directive {
+              name: String!
+              description: String
+              isRepeatable: Boolean!
+              locations: [__DirectiveLocation!]!
+              args: [__InputValue!]!
+            }
+
+            """
+            A Directive can be adjacent to many parts of the GraphQL language, a __DirectiveLocation describes one such possible adjacencies.
+            """
+            enum __DirectiveLocation {
+              """Location adjacent to a query operation."""
+              QUERY
+
+              """Location adjacent to a mutation operation."""
+              MUTATION
+
+              """Location adjacent to a subscription operation."""
+              SUBSCRIPTION
+
+              """Location adjacent to a field."""
+              FIELD
+
+              """Location adjacent to a fragment definition."""
+              FRAGMENT_DEFINITION
+
+              """Location adjacent to a fragment spread."""
+              FRAGMENT_SPREAD
+
+              """Location adjacent to an inline fragment."""
+              INLINE_FRAGMENT
+
+              """Location adjacent to a variable definition."""
+              VARIABLE_DEFINITION
+
+              """Location adjacent to a schema definition."""
+              SCHEMA
+
+              """Location adjacent to a scalar definition."""
+              SCALAR
+
+              """Location adjacent to an object type definition."""
+              OBJECT
+
+              """Location adjacent to a field definition."""
+              FIELD_DEFINITION
+
+              """Location adjacent to an argument definition."""
+              ARGUMENT_DEFINITION
+
+              """Location adjacent to an interface definition."""
+              INTERFACE
+
+              """Location adjacent to a union definition."""
+              UNION
+
+              """Location adjacent to an enum definition."""
+              ENUM
+
+              """Location adjacent to an enum value definition."""
+              ENUM_VALUE
+
+              """Location adjacent to an input object type definition."""
+              INPUT_OBJECT
+
+              """Location adjacent to an input object field definition."""
+              INPUT_FIELD_DEFINITION
+            }
+            '''  # noqa: E501
+        )
+
+
+def describe_print_value():
+    def print_value_convenience_function():
+        assert print_value(1.5, GraphQLFloat) == "1.5"
+        assert print_value("foo", GraphQLString) == '"foo"'
diff --git a/tests/utilities/test_separate_operations.py b/tests/utilities/test_separate_operations.py
new file mode 100644
index 0000000..e775877
--- /dev/null
+++ b/tests/utilities/test_separate_operations.py
@@ -0,0 +1,172 @@
+from graphql.language import parse, print_ast
+from graphql.utilities import separate_operations
+
+from ..utils import dedent
+
+
+def describe_separate_operations():
+    def separates_one_ast_into_multiple_maintaining_document_order():
+        ast = parse(
+            """
+            {
+              ...Y
+              ...X
+            }
+
+            query One {
+              foo
+              bar
+              ...A
+              ...X
+            }
+
+            fragment A on T {
+              field
+              ...B
+            }
+
+            fragment X on T {
+              fieldX
+            }
+
+            query Two {
+              ...A
+              ...Y
+              baz
+            }
+
+            fragment Y on T {
+              fieldY
+            }
+
+            fragment B on T {
+              something
+            }
+
+            """
+        )
+
+        separated_asts = separate_operations(ast)
+
+        assert list(separated_asts) == ["", "One", "Two"]
+
+        assert print_ast(separated_asts[""]) == dedent(
+            """
+            {
+              ...Y
+              ...X
+            }
+
+            fragment X on T {
+              fieldX
+            }
+
+            fragment Y on T {
+              fieldY
+            }
+            """
+        )
+
+        assert print_ast(separated_asts["One"]) == dedent(
+            """
+            query One {
+              foo
+              bar
+              ...A
+              ...X
+            }
+
+            fragment A on T {
+              field
+              ...B
+            }
+
+            fragment X on T {
+              fieldX
+            }
+
+            fragment B on T {
+              something
+            }
+            """
+        )
+
+        assert print_ast(separated_asts["Two"]) == dedent(
+            """
+            fragment A on T {
+              field
+              ...B
+            }
+
+            query Two {
+              ...A
+              ...Y
+              baz
+            }
+
+            fragment Y on T {
+              fieldY
+            }
+
+            fragment B on T {
+              something
+            }
+            """
+        )
+
+    def survives_circular_dependencies():
+        ast = parse(
+            """
+            query One {
+              ...A
+            }
+
+            fragment A on T {
+              ...B
+            }
+
+            fragment B on T {
+              ...A
+            }
+
+            query Two {
+              ...B
+            }
+            """
+        )
+
+        separated_asts = separate_operations(ast)
+
+        assert list(separated_asts) == ["One", "Two"]
+
+        assert print_ast(separated_asts["One"]) == dedent(
+            """
+            query One {
+              ...A
+            }
+
+            fragment A on T {
+              ...B
+            }
+
+            fragment B on T {
+              ...A
+            }
+            """
+        )
+
+        assert print_ast(separated_asts["Two"]) == dedent(
+            """
+            fragment A on T {
+              ...B
+            }
+
+            fragment B on T {
+              ...A
+            }
+
+            query Two {
+              ...B
+            }
+            """
+        )
diff --git a/tests/utilities/test_strip_ignored_characters.py b/tests/utilities/test_strip_ignored_characters.py
new file mode 100644
index 0000000..32ddede
--- /dev/null
+++ b/tests/utilities/test_strip_ignored_characters.py
@@ -0,0 +1,397 @@
+from json import dumps
+from typing import Optional
+
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLSyntaxError
+from graphql.language import Lexer, Source, TokenKind, parse
+from graphql.utilities import strip_ignored_characters
+
+from ..fixtures import kitchen_sink_query, kitchen_sink_sdl  # noqa: F401
+from ..utils import dedent
+
+ignored_tokens = [
+    # UnicodeBOM
+    "\uFEFF",  # Byte Order Mark (U+FEFF)
+    # WhiteSpace
+    "\t",  # Horizontal Tab (U+0009)
+    " ",  # Space (U+0020)
+    # LineTerminator
+    "\n",  # "New Line (U+000A)"
+    "\r",  # "Carriage Return (U+000D)" [ lookahead ! "New Line (U+000A)" ]
+    "\r\n",  # "Carriage Return (U+000D)" "New Line (U+000A)"
+    # Comment
+    '# "Comment" string\n',  # `#` CommentChar*
+    # Comma
+    ",",  # ,
+]
+
+punctuator_tokens = ["!", "$", "(", ")", "...", ":", "=", "@", "[", "]", "{", "|", "}"]
+
+non_punctuator_tokens = [
+    "name_token",  # Name
+    "1",  # IntValue
+    "3.14",  # FloatValue
+    '"some string value"',  # StringValue
+    '"""block\nstring\nvalue"""',  # StringValue(BlockString)
+]
+
+
+def lex_value(s: str) -> Optional[str]:
+    lexer = Lexer(Source(s))
+    value = lexer.advance().value
+    assert lexer.advance().kind == TokenKind.EOF, "Expected EOF"
+    return value
+
+
+class ExpectStripped:
+    def __init__(self, doc_string: str):
+        self.doc_string = doc_string
+
+    def to_equal(self, expected: str):
+        doc_string = self.doc_string
+        stripped = strip_ignored_characters(doc_string)
+
+        assert stripped == expected, dedent(
+            f"""
+            Expected strip_ignored_characters({doc_string!r})
+              to equal {expected!r}
+              but got {stripped!r}
+            """
+        )
+
+        stripped_twice = strip_ignored_characters(stripped)
+
+        assert stripped == stripped_twice, dedent(
+            f""""
+            Expected strip_ignored_characters({stripped!r})"
+              to equal {stripped!r}
+              but got {stripped_twice!r}
+            """
+        )
+
+    def to_stay_the_same(self):
+        self.to_equal(self.doc_string)
+
+
+def describe_strip_ignored_characters():
+    def asserts_that_a_source_was_provided():
+        with raises(
+            TypeError, match="missing 1 required positional argument: 'source'"
+        ):
+            # noinspection PyArgumentList
+            strip_ignored_characters()  # type: ignore
+        with raises(
+            TypeError, match="Must provide string or Source. Received: None\\."
+        ):
+            # noinspection PyTypeChecker
+            strip_ignored_characters(None)  # type: ignore
+
+    def asserts_that_a_valid_source_was_provided():
+        with raises(TypeError, match="Must provide string or Source. Received: {}\\."):
+            # noinspection PyTypeChecker
+            strip_ignored_characters({})  # type: ignore
+
+    def strips_ignored_characters_from_graphql_query_document():
+        query = dedent(
+            """
+            query SomeQuery($foo: String!, $bar: String) {
+              someField(foo: $foo, bar: $bar) {
+                a
+                b {
+                  c
+                  d
+                }
+              }
+            }
+            """
+        )
+
+        assert strip_ignored_characters(query) == (
+            "query SomeQuery($foo:String!$bar:String)"
+            "{someField(foo:$foo bar:$bar){a b{c d}}}"
+        )
+
+    def strips_ignored_characters_from_graphql_sdl_document():
+        sdl = dedent(
+            '''
+            """
+            Type description
+            """
+            type Foo {
+              """
+              Field description
+              """
+              bar: String
+            }
+          '''
+        )
+
+        assert strip_ignored_characters(sdl) == (
+            '"""Type description""" type Foo{"""Field description""" bar:String}'
+        )
+
+    def report_document_with_invalid_token():
+        with raises(GraphQLSyntaxError) as exc_info:
+            strip_ignored_characters('{ foo(arg: "\n"')
+
+        assert str(exc_info.value) + "\n" == dedent(
+            """
+            Syntax Error: Unterminated string.
+
+            GraphQL request:1:13
+            1 | { foo(arg: "
+              |             ^
+            2 | "
+            """
+        )
+
+    def strips_non_parsable_document():
+        ExpectStripped('{ foo(arg: "str"').to_equal('{foo(arg:"str"')
+
+    def strips_documents_with_only_ignored_characters():
+        ExpectStripped("\n").to_equal("")
+        ExpectStripped(",").to_equal("")
+        ExpectStripped(",,").to_equal("")
+        ExpectStripped("#comment\n, \n").to_equal("")
+
+        for ignored in ignored_tokens:
+            ExpectStripped(ignored).to_equal("")
+
+            for another_ignored in ignored_tokens:
+                ExpectStripped(ignored + another_ignored).to_equal("")
+
+        ExpectStripped("".join(ignored_tokens)).to_equal("")
+
+    def strips_leading_and_trailing_ignored_tokens():
+        ExpectStripped("\n1").to_equal("1")
+        ExpectStripped(",1").to_equal("1")
+        ExpectStripped(",,1").to_equal("1")
+        ExpectStripped("#comment\n, \n1").to_equal("1")
+
+        ExpectStripped("1\n").to_equal("1")
+        ExpectStripped("1,").to_equal("1")
+        ExpectStripped("1,,").to_equal("1")
+        ExpectStripped("1#comment\n, \n").to_equal("1")
+
+        for token in punctuator_tokens + non_punctuator_tokens:
+            for ignored in ignored_tokens:
+                ExpectStripped(ignored + token).to_equal(token)
+                ExpectStripped(token + ignored).to_equal(token)
+
+                for another_ignored in ignored_tokens:
+                    ExpectStripped(token + ignored + ignored).to_equal(token)
+                    ExpectStripped(ignored + another_ignored + token).to_equal(token)
+
+            ExpectStripped("".join(ignored_tokens) + token).to_equal(token)
+            ExpectStripped(token + "".join(ignored_tokens)).to_equal(token)
+
+    def strips_ignored_tokens_between_punctuator_tokens():
+        ExpectStripped("[,)").to_equal("[)")
+        ExpectStripped("[\r)").to_equal("[)")
+        ExpectStripped("[\r\r)").to_equal("[)")
+        ExpectStripped("[\r,)").to_equal("[)")
+        ExpectStripped("[,\n)").to_equal("[)")
+
+        for left in punctuator_tokens:
+            for right in punctuator_tokens:
+                for ignored in ignored_tokens:
+                    ExpectStripped(left + ignored + right).to_equal(left + right)
+
+                    for another_ignored in ignored_tokens:
+                        ExpectStripped(
+                            left + ignored + another_ignored + right
+                        ).to_equal(left + right)
+
+                ExpectStripped(left + "".join(ignored_tokens) + right).to_equal(
+                    left + right
+                )
+
+    def strips_ignored_tokens_between_punctuator_and_non_punctuator_tokens():
+        ExpectStripped("[,1").to_equal("[1")
+        ExpectStripped("[\r1").to_equal("[1")
+        ExpectStripped("[\r\r1").to_equal("[1")
+        ExpectStripped("[\r,1").to_equal("[1")
+        ExpectStripped("[,\n1").to_equal("[1")
+
+        for non_punctuator in non_punctuator_tokens:
+            for punctuator in punctuator_tokens:
+                for ignored in ignored_tokens:
+                    ExpectStripped(punctuator + ignored + non_punctuator).to_equal(
+                        punctuator + non_punctuator
+                    )
+
+                    for another_ignored in ignored_tokens:
+                        ExpectStripped(
+                            punctuator + ignored + another_ignored + non_punctuator
+                        ).to_equal(punctuator + non_punctuator)
+
+                ExpectStripped(
+                    punctuator + "".join(ignored_tokens) + non_punctuator
+                ).to_equal(punctuator + non_punctuator)
+
+    def strips_ignored_tokens_between_non_punctuator_and_punctuator_tokens():
+        ExpectStripped("1,[").to_equal("1[")
+        ExpectStripped("1\r[").to_equal("1[")
+        ExpectStripped("1\r\r[").to_equal("1[")
+        ExpectStripped("1\r,[").to_equal("1[")
+        ExpectStripped("1,\n[").to_equal("1[")
+
+        for non_punctuator in non_punctuator_tokens:
+            for punctuator in punctuator_tokens:
+                # Special case for that is handled in the below test
+                if punctuator == "...":
+                    continue
+
+                for ignored in ignored_tokens:
+                    ExpectStripped(non_punctuator + ignored + punctuator).to_equal(
+                        non_punctuator + punctuator
+                    )
+
+                    for another_ignored in ignored_tokens:
+                        ExpectStripped(
+                            non_punctuator + ignored + another_ignored + punctuator
+                        ).to_equal(non_punctuator + punctuator)
+
+                ExpectStripped(
+                    non_punctuator + "".join(ignored_tokens) + punctuator
+                ).to_equal(non_punctuator + punctuator)
+
+    def replace_ignored_tokens_between_non_punctuator_tokens_and_spread_with_space():
+        ExpectStripped("a ...").to_equal("a ...")
+        ExpectStripped("1 ...").to_equal("1 ...")
+        ExpectStripped("1 ... ...").to_equal("1 ......")
+
+        for non_punctuator in non_punctuator_tokens:
+            for ignored in ignored_tokens:
+                ExpectStripped(non_punctuator + ignored + "...").to_equal(
+                    non_punctuator + " ..."
+                )
+
+                for another_ignored in ignored_tokens:
+                    ExpectStripped(
+                        non_punctuator + ignored + another_ignored + " ..."
+                    ).to_equal(non_punctuator + " ...")
+
+            ExpectStripped(non_punctuator + "".join(ignored_tokens) + "...").to_equal(
+                non_punctuator + " ..."
+            )
+
+    def replace_ignored_tokens_between_non_punctuator_tokens_with_space():
+        ExpectStripped("1 2").to_stay_the_same()
+        ExpectStripped('"" ""').to_stay_the_same()
+        ExpectStripped("a b").to_stay_the_same()
+
+        ExpectStripped("a,1").to_equal("a 1")
+        ExpectStripped("a,,1").to_equal("a 1")
+        ExpectStripped("a  1").to_equal("a 1")
+        ExpectStripped("a \t 1").to_equal("a 1")
+
+        for left in non_punctuator_tokens:
+            for right in non_punctuator_tokens:
+                for ignored in ignored_tokens:
+                    ExpectStripped(left + ignored + right).to_equal(left + " " + right)
+
+                    for another_ignored in ignored_tokens:
+                        ExpectStripped(
+                            left + ignored + another_ignored + right
+                        ).to_equal(left + " " + right)
+
+                ExpectStripped(left + "".join(ignored_tokens) + right).to_equal(
+                    left + " " + right
+                )
+
+    def does_not_strip_ignored_tokens_embedded_in_the_string():
+        ExpectStripped('" "').to_stay_the_same()
+        ExpectStripped('","').to_stay_the_same()
+        ExpectStripped('",,"').to_stay_the_same()
+        ExpectStripped('",|"').to_stay_the_same()
+
+        for ignored in ignored_tokens:
+            ExpectStripped(dumps(ignored)).to_stay_the_same()
+
+            for another_ignored in ignored_tokens:
+                ExpectStripped(dumps(ignored + another_ignored)).to_stay_the_same()
+
+        ExpectStripped(dumps("".join(ignored_tokens))).to_stay_the_same()
+
+    def does_not_strip_ignored_tokens_embedded_in_the_block_string():
+        ExpectStripped('""","""').to_stay_the_same()
+        ExpectStripped('""",,"""').to_stay_the_same()
+        ExpectStripped('""",|"""').to_stay_the_same()
+
+        ignored_tokens_without_formatting = [
+            token
+            for token in ignored_tokens
+            if token not in ["\n", "\r", "\r\n", "\t", " "]
+        ]
+
+        for ignored in ignored_tokens_without_formatting:
+            ExpectStripped('"""|' + ignored + '|"""').to_stay_the_same()
+
+            for another_ignored in ignored_tokens_without_formatting:
+                ExpectStripped(
+                    '"""|' + ignored + another_ignored + '|"""'
+                ).to_stay_the_same()
+
+        ExpectStripped(
+            '"""|' + "".join(ignored_tokens_without_formatting) + '|"""'
+        ).to_stay_the_same()
+
+    def strips_ignored_characters_inside_block_strings():
+        # noinspection PyShadowingNames
+        def expect_stripped_string(block_str: str):
+            original_value = lex_value(block_str)
+            stripped_value = lex_value(strip_ignored_characters(block_str))
+
+            assert original_value == stripped_value, dedent(
+                f"""
+                Expected lexValue(stripIgnoredCharacters({block_str!r})
+                  to equal {original_value!r}
+                  but got {stripped_value!r}
+                """
+            )
+            return ExpectStripped(block_str)
+
+        expect_stripped_string('""""""').to_stay_the_same()
+        expect_stripped_string('""" """').to_equal('""""""')
+
+        expect_stripped_string('"""a"""').to_stay_the_same()
+        expect_stripped_string('""" a"""').to_equal('""" a"""')
+        expect_stripped_string('""" a """').to_equal('""" a """')
+
+        expect_stripped_string('"""\n"""').to_equal('""""""')
+        expect_stripped_string('"""a\nb"""').to_equal('"""a\nb"""')
+        expect_stripped_string('"""a\rb"""').to_equal('"""a\nb"""')
+        expect_stripped_string('"""a\r\nb"""').to_equal('"""a\nb"""')
+        expect_stripped_string('"""a\r\n\nb"""').to_equal('"""a\n\nb"""')
+
+        expect_stripped_string('"""\\\n"""').to_stay_the_same()
+        expect_stripped_string('""""\n"""').to_stay_the_same()
+        expect_stripped_string('"""\\"""\n"""').to_equal('"""\\""""""')
+
+        expect_stripped_string('"""\na\n b"""').to_stay_the_same()
+        expect_stripped_string('"""\n a\n b"""').to_equal('"""a\nb"""')
+        expect_stripped_string('"""\na\n b\nc"""').to_equal('"""a\n b\nc"""')
+
+    # noinspection PyShadowingNames
+    def strips_kitchen_sink_query_but_maintains_the_exact_same_ast(
+        kitchen_sink_query,  # noqa: F811
+    ):
+        stripped_query = strip_ignored_characters(kitchen_sink_query)
+        assert strip_ignored_characters(stripped_query) == stripped_query
+
+        query_ast = parse(kitchen_sink_query, no_location=True)
+        stripped_ast = parse(stripped_query, no_location=True)
+        assert stripped_ast == query_ast
+
+    # noinspection PyShadowingNames
+    def strips_kitchen_sink_sdl_but_maintains_the_exact_same_ast(
+        kitchen_sink_sdl,  # noqa: F811
+    ):
+        stripped_sdl = strip_ignored_characters(kitchen_sink_sdl)
+        assert strip_ignored_characters(stripped_sdl) == stripped_sdl
+
+        sdl_ast = parse(kitchen_sink_sdl, no_location=True)
+        stripped_ast = parse(stripped_sdl, no_location=True)
+        assert stripped_ast == sdl_ast
diff --git a/tests/utilities/test_strip_ignored_characters_fuzz.py b/tests/utilities/test_strip_ignored_characters_fuzz.py
new file mode 100644
index 0000000..475a5f7
--- /dev/null
+++ b/tests/utilities/test_strip_ignored_characters_fuzz.py
@@ -0,0 +1,41 @@
+from typing import Optional
+
+from pytest import mark  # type: ignore
+
+from graphql.error import GraphQLSyntaxError
+from graphql.language import Lexer, Source, TokenKind
+from graphql.utilities import strip_ignored_characters
+
+from ..utils import dedent, gen_fuzz_strings
+
+
+def lex_value(s: str) -> Optional[str]:
+    lexer = Lexer(Source(s))
+    value = lexer.advance().value
+    assert lexer.advance().kind == TokenKind.EOF, "Expected EOF"
+    return value
+
+
+def describe_strip_ignored_characters():
+    @mark.slow
+    @mark.timeout(20)
+    def strips_ignored_characters_inside_random_block_strings():
+        # Testing with length >7 is taking exponentially more time. However it is
+        # highly recommended to test with increased limit if you make any change.
+        for fuzz_str in gen_fuzz_strings(allowed_chars='\n\t "a\\', max_length=7):
+            test_str = f'"""{fuzz_str}"""'
+
+            try:
+                test_value = lex_value(test_str)
+            except (AssertionError, GraphQLSyntaxError):
+                continue  # skip invalid values
+
+            stripped_value = lex_value(strip_ignored_characters(test_str))
+
+            assert test_value == stripped_value, dedent(
+                f"""
+                Expected lexValue(stripIgnoredCharacters({test_str!r})
+                  to equal {test_value!r}
+                  but got {stripped_value!r}
+                """
+            )
diff --git a/tests/utilities/test_type_comparators.py b/tests/utilities/test_type_comparators.py
new file mode 100644
index 0000000..8310c78
--- /dev/null
+++ b/tests/utilities/test_type_comparators.py
@@ -0,0 +1,112 @@
+from graphql.type import (
+    GraphQLField,
+    GraphQLFloat,
+    GraphQLInt,
+    GraphQLInterfaceType,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLObjectType,
+    GraphQLOutputType,
+    GraphQLSchema,
+    GraphQLString,
+    GraphQLUnionType,
+)
+from graphql.utilities import is_equal_type, is_type_sub_type_of
+
+
+def describe_type_comparators():
+    def describe_is_equal_type():
+        def same_references_are_equal():
+            assert is_equal_type(GraphQLString, GraphQLString) is True
+
+        def int_and_float_are_not_equal():
+            assert is_equal_type(GraphQLInt, GraphQLFloat) is False
+
+        def lists_of_same_type_are_equal():
+            assert (
+                is_equal_type(GraphQLList(GraphQLInt), GraphQLList(GraphQLInt)) is True
+            )
+
+        def lists_is_not_equal_to_item():
+            assert is_equal_type(GraphQLList(GraphQLInt), GraphQLInt) is False
+
+        def nonnull_of_same_type_are_equal():
+            assert (
+                is_equal_type(GraphQLNonNull(GraphQLInt), GraphQLNonNull(GraphQLInt))
+                is True
+            )
+
+        def nonnull_is_not_equal_to_nullable():
+            assert is_equal_type(GraphQLNonNull(GraphQLInt), GraphQLInt) is False
+
+    def describe_is_type_sub_type_of():
+        def _test_schema(field_type: GraphQLOutputType = GraphQLString):
+            return GraphQLSchema(
+                query=GraphQLObjectType("Query", {"field": GraphQLField(field_type)})
+            )
+
+        def same_reference_is_subtype():
+            assert (
+                is_type_sub_type_of(_test_schema(), GraphQLString, GraphQLString)
+                is True
+            )
+
+        def int_is_not_subtype_of_float():
+            assert (
+                is_type_sub_type_of(_test_schema(), GraphQLInt, GraphQLFloat) is False
+            )
+
+        def non_null_is_subtype_of_nullable():
+            assert (
+                is_type_sub_type_of(
+                    _test_schema(), GraphQLNonNull(GraphQLInt), GraphQLInt
+                )
+                is True
+            )
+
+        def nullable_is_not_subtype_of_non_null():
+            assert (
+                is_type_sub_type_of(
+                    _test_schema(), GraphQLInt, GraphQLNonNull(GraphQLInt)
+                )
+                is False
+            )
+
+        def item_is_not_subtype_of_list():
+            assert not is_type_sub_type_of(
+                _test_schema(), GraphQLInt, GraphQLList(GraphQLInt)
+            )
+
+        def list_is_not_subtype_of_item():
+            assert not is_type_sub_type_of(
+                _test_schema(), GraphQLList(GraphQLInt), GraphQLInt
+            )
+
+        def member_is_subtype_of_union():
+            member = GraphQLObjectType("Object", {"field": GraphQLField(GraphQLString)})
+            union = GraphQLUnionType("Union", [member])
+            schema = _test_schema(union)
+            assert is_type_sub_type_of(schema, member, union)
+
+        def implementing_object_is_subtype_of_interface():
+            iface = GraphQLInterfaceType(
+                "Interface", {"field": GraphQLField(GraphQLString)}
+            )
+            impl = GraphQLObjectType(
+                "Object", {"field": GraphQLField(GraphQLString)}, [iface],
+            )
+            schema = _test_schema(impl)
+            assert is_type_sub_type_of(schema, impl, iface)
+
+        def implementing_interface_is_subtype_of_interface():
+            iface = GraphQLInterfaceType(
+                "Interface", {"field": GraphQLField(GraphQLString)}
+            )
+            iface2 = GraphQLInterfaceType(
+                "Interface2", {"field": GraphQLField(GraphQLString)}, [iface]
+            )
+            impl = GraphQLObjectType(
+                "Object", {"field": GraphQLField(GraphQLString)}, [iface2, iface],
+            )
+            schema = _test_schema(impl)
+            assert is_type_sub_type_of(schema, iface2, iface)
diff --git a/tests/utilities/test_type_from_ast.py b/tests/utilities/test_type_from_ast.py
new file mode 100644
index 0000000..53ffbe9
--- /dev/null
+++ b/tests/utilities/test_type_from_ast.py
@@ -0,0 +1,38 @@
+from pytest import raises  # type: ignore
+
+from graphql.language import parse_type, TypeNode
+from graphql.type import GraphQLList, GraphQLNonNull, GraphQLObjectType
+from graphql.utilities import type_from_ast
+
+from ..validation.harness import test_schema
+
+
+def describe_type_from_ast():
+    def for_named_type_node():
+        node = parse_type("Cat")
+        type_for_node = type_from_ast(test_schema, node)
+        assert isinstance(type_for_node, GraphQLObjectType)
+        assert type_for_node.name == "Cat"
+
+    def for_list_type_node():
+        node = parse_type("[Cat]")
+        type_for_node = type_from_ast(test_schema, node)
+        assert isinstance(type_for_node, GraphQLList)
+        of_type = type_for_node.of_type
+        assert isinstance(of_type, GraphQLObjectType)
+        assert of_type.name == "Cat"
+
+    def for_non_null_type_node():
+        node = parse_type("Cat!")
+        type_for_node = type_from_ast(test_schema, node)
+        assert isinstance(type_for_node, GraphQLNonNull)
+        of_type = type_for_node.of_type
+        assert isinstance(of_type, GraphQLObjectType)
+        assert of_type.name == "Cat"
+
+    def for_unspecified_type_node():
+        node = TypeNode()
+        with raises(TypeError) as exc_info:
+            type_from_ast(test_schema, node)
+        msg = str(exc_info.value)
+        assert msg == "Unexpected type node: <TypeNode instance>."
diff --git a/tests/utilities/test_type_info.py b/tests/utilities/test_type_info.py
new file mode 100644
index 0000000..910c466
--- /dev/null
+++ b/tests/utilities/test_type_info.py
@@ -0,0 +1,414 @@
+from graphql.language import (
+    FieldNode,
+    NameNode,
+    Node,
+    OperationDefinitionNode,
+    SelectionSetNode,
+    parse,
+    parse_value,
+    print_ast,
+    visit,
+    Visitor,
+)
+from graphql.type import get_named_type, is_composite_type
+from graphql.utilities import TypeInfo, TypeInfoVisitor, build_schema
+
+from ..validation.harness import test_schema
+
+from ..fixtures import kitchen_sink_query  # noqa: F401
+
+
+def describe_type_info():
+    def allow_all_methods_to_be_called_before_entering_any_mode():
+        type_info = TypeInfo(test_schema)
+
+        assert type_info.get_type() is None
+        assert type_info.get_parent_type() is None
+        assert type_info.get_input_type() is None
+        assert type_info.get_parent_input_type() is None
+        assert type_info.get_field_def() is None
+        assert type_info.get_default_value() is None
+        assert type_info.get_directive() is None
+        assert type_info.get_argument() is None
+        assert type_info.get_enum_value() is None
+
+
+def describe_visit_with_type_info():
+    def supports_different_operation_types():
+        schema = build_schema(
+            """
+            schema {
+              query: QueryRoot
+              mutation: MutationRoot
+              subscription: SubscriptionRoot
+            }
+
+            type QueryRoot {
+              foo: String
+            }
+
+            type MutationRoot {
+              bar: String
+            }
+
+            type SubscriptionRoot {
+              baz: String
+            }
+            """
+        )
+        ast = parse(
+            """
+            query { foo }
+            mutation { bar }
+            subscription { baz }
+            """
+        )
+
+        class TestVisitor(Visitor):
+            def __init__(self):
+                self.root_types = {}
+
+            def enter_operation_definition(self, node: OperationDefinitionNode, *_args):
+                self.root_types[node.operation.value] = str(type_info.get_type())
+
+        type_info = TypeInfo(schema)
+        test_visitor = TestVisitor()
+        assert visit(ast, TypeInfoVisitor(type_info, test_visitor))
+
+        assert test_visitor.root_types == {
+            "query": "QueryRoot",
+            "mutation": "MutationRoot",
+            "subscription": "SubscriptionRoot",
+        }
+
+    def provide_exact_same_arguments_to_wrapped_visitor():
+        ast = parse("{ human(id: 4) { name, pets { ... { name } }, unknown } }")
+
+        class TestVisitor(Visitor):
+            def __init__(self):
+                self.args = []
+
+            def enter(self, *args):
+                self.args.append(("enter", *args))
+
+            def leave(self, *args):
+                self.args.append(("leave", *args))
+
+        test_visitor = TestVisitor()
+        visit(ast, test_visitor)
+
+        type_info = TypeInfo(test_schema)
+        wrapped_visitor = TestVisitor()
+        visit(ast, TypeInfoVisitor(type_info, wrapped_visitor))
+
+        assert test_visitor.args == wrapped_visitor.args
+
+    def maintains_type_info_during_visit():
+        visited = []
+
+        type_info = TypeInfo(test_schema)
+
+        ast = parse("{ human(id: 4) { name, pets { ... { name } }, unknown } }")
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                parent_type = type_info.get_parent_type()
+                type_ = type_info.get_type()
+                input_type = type_info.get_input_type()
+                node = args[0]
+                visited.append(
+                    (
+                        "enter",
+                        node.kind,
+                        node.value if node.kind == "name" else None,
+                        str(parent_type) if parent_type else None,
+                        str(type_) if type_ else None,
+                        str(input_type) if input_type else None,
+                    )
+                )
+
+            @staticmethod
+            def leave(*args):
+                parent_type = type_info.get_parent_type()
+                type_ = type_info.get_type()
+                input_type = type_info.get_input_type()
+                node = args[0]
+                visited.append(
+                    (
+                        "leave",
+                        node.kind,
+                        node.value if node.kind == "name" else None,
+                        str(parent_type) if parent_type else None,
+                        str(type_) if type_ else None,
+                        str(input_type) if input_type else None,
+                    )
+                )
+
+        visit(ast, TypeInfoVisitor(type_info, TestVisitor()))
+
+        assert visited == [
+            ("enter", "document", None, None, None, None),
+            ("enter", "operation_definition", None, None, "QueryRoot", None),
+            ("enter", "selection_set", None, "QueryRoot", "QueryRoot", None),
+            ("enter", "field", None, "QueryRoot", "Human", None),
+            ("enter", "name", "human", "QueryRoot", "Human", None),
+            ("leave", "name", "human", "QueryRoot", "Human", None),
+            ("enter", "argument", None, "QueryRoot", "Human", "ID"),
+            ("enter", "name", "id", "QueryRoot", "Human", "ID"),
+            ("leave", "name", "id", "QueryRoot", "Human", "ID"),
+            ("enter", "int_value", None, "QueryRoot", "Human", "ID"),
+            ("leave", "int_value", None, "QueryRoot", "Human", "ID"),
+            ("leave", "argument", None, "QueryRoot", "Human", "ID"),
+            ("enter", "selection_set", None, "Human", "Human", None),
+            ("enter", "field", None, "Human", "String", None),
+            ("enter", "name", "name", "Human", "String", None),
+            ("leave", "name", "name", "Human", "String", None),
+            ("leave", "field", None, "Human", "String", None),
+            ("enter", "field", None, "Human", "[Pet]", None),
+            ("enter", "name", "pets", "Human", "[Pet]", None),
+            ("leave", "name", "pets", "Human", "[Pet]", None),
+            ("enter", "selection_set", None, "Pet", "[Pet]", None),
+            ("enter", "inline_fragment", None, "Pet", "Pet", None),
+            ("enter", "selection_set", None, "Pet", "Pet", None),
+            ("enter", "field", None, "Pet", "String", None),
+            ("enter", "name", "name", "Pet", "String", None),
+            ("leave", "name", "name", "Pet", "String", None),
+            ("leave", "field", None, "Pet", "String", None),
+            ("leave", "selection_set", None, "Pet", "Pet", None),
+            ("leave", "inline_fragment", None, "Pet", "Pet", None),
+            ("leave", "selection_set", None, "Pet", "[Pet]", None),
+            ("leave", "field", None, "Human", "[Pet]", None),
+            ("enter", "field", None, "Human", None, None),
+            ("enter", "name", "unknown", "Human", None, None),
+            ("leave", "name", "unknown", "Human", None, None),
+            ("leave", "field", None, "Human", None, None),
+            ("leave", "selection_set", None, "Human", "Human", None),
+            ("leave", "field", None, "QueryRoot", "Human", None),
+            ("leave", "selection_set", None, "QueryRoot", "QueryRoot", None),
+            ("leave", "operation_definition", None, None, "QueryRoot", None),
+            ("leave", "document", None, None, None, None),
+        ]
+
+    def maintains_type_info_during_edit():
+        visited = []
+        type_info = TypeInfo(test_schema)
+
+        ast = parse("{ human(id: 4) { name, pets }, alien }")
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(*args):
+                parent_type = type_info.get_parent_type()
+                type_ = type_info.get_type()
+                input_type = type_info.get_input_type()
+                node = args[0]
+                visited.append(
+                    (
+                        "enter",
+                        node.kind,
+                        node.value if node.kind == "name" else None,
+                        str(parent_type) if parent_type else None,
+                        str(type_) if type_ else None,
+                        str(input_type) if input_type else None,
+                    )
+                )
+
+                # Make a query valid by adding missing selection sets.
+                if (
+                    node.kind == "field"
+                    and not node.selection_set
+                    and is_composite_type(get_named_type(type_))
+                ):
+                    return FieldNode(
+                        alias=node.alias,
+                        name=node.name,
+                        arguments=node.arguments,
+                        directives=node.directives,
+                        selection_set=SelectionSetNode(
+                            selections=[FieldNode(name=NameNode(value="__typename"))]
+                        ),
+                    )
+
+            @staticmethod
+            def leave(*args):
+                parent_type = type_info.get_parent_type()
+                type_ = type_info.get_type()
+                input_type = type_info.get_input_type()
+                node = args[0]
+                visited.append(
+                    (
+                        "leave",
+                        node.kind,
+                        node.value if node.kind == "name" else None,
+                        str(parent_type) if parent_type else None,
+                        str(type_) if type_ else None,
+                        str(input_type) if input_type else None,
+                    )
+                )
+
+        edited_ast = visit(ast, TypeInfoVisitor(type_info, TestVisitor()))
+
+        assert ast == parse("{ human(id: 4) { name, pets }, alien }")
+
+        assert print_ast(edited_ast) == print_ast(
+            parse(
+                "{ human(id: 4) { name, pets { __typename } },"
+                " alien { __typename } }"
+            )
+        )
+
+        assert visited == [
+            ("enter", "document", None, None, None, None),
+            ("enter", "operation_definition", None, None, "QueryRoot", None),
+            ("enter", "selection_set", None, "QueryRoot", "QueryRoot", None),
+            ("enter", "field", None, "QueryRoot", "Human", None),
+            ("enter", "name", "human", "QueryRoot", "Human", None),
+            ("leave", "name", "human", "QueryRoot", "Human", None),
+            ("enter", "argument", None, "QueryRoot", "Human", "ID"),
+            ("enter", "name", "id", "QueryRoot", "Human", "ID"),
+            ("leave", "name", "id", "QueryRoot", "Human", "ID"),
+            ("enter", "int_value", None, "QueryRoot", "Human", "ID"),
+            ("leave", "int_value", None, "QueryRoot", "Human", "ID"),
+            ("leave", "argument", None, "QueryRoot", "Human", "ID"),
+            ("enter", "selection_set", None, "Human", "Human", None),
+            ("enter", "field", None, "Human", "String", None),
+            ("enter", "name", "name", "Human", "String", None),
+            ("leave", "name", "name", "Human", "String", None),
+            ("leave", "field", None, "Human", "String", None),
+            ("enter", "field", None, "Human", "[Pet]", None),
+            ("enter", "name", "pets", "Human", "[Pet]", None),
+            ("leave", "name", "pets", "Human", "[Pet]", None),
+            ("enter", "selection_set", None, "Pet", "[Pet]", None),
+            ("enter", "field", None, "Pet", "String!", None),
+            ("enter", "name", "__typename", "Pet", "String!", None),
+            ("leave", "name", "__typename", "Pet", "String!", None),
+            ("leave", "field", None, "Pet", "String!", None),
+            ("leave", "selection_set", None, "Pet", "[Pet]", None),
+            ("leave", "field", None, "Human", "[Pet]", None),
+            ("leave", "selection_set", None, "Human", "Human", None),
+            ("leave", "field", None, "QueryRoot", "Human", None),
+            ("enter", "field", None, "QueryRoot", "Alien", None),
+            ("enter", "name", "alien", "QueryRoot", "Alien", None),
+            ("leave", "name", "alien", "QueryRoot", "Alien", None),
+            ("enter", "selection_set", None, "Alien", "Alien", None),
+            ("enter", "field", None, "Alien", "String!", None),
+            ("enter", "name", "__typename", "Alien", "String!", None),
+            ("leave", "name", "__typename", "Alien", "String!", None),
+            ("leave", "field", None, "Alien", "String!", None),
+            ("leave", "selection_set", None, "Alien", "Alien", None),
+            ("leave", "field", None, "QueryRoot", "Alien", None),
+            ("leave", "selection_set", None, "QueryRoot", "QueryRoot", None),
+            ("leave", "operation_definition", None, None, "QueryRoot", None),
+            ("leave", "document", None, None, None, None),
+        ]
+
+    def supports_traversal_of_input_values():
+        visited = []
+
+        complex_input_type = test_schema.get_type("ComplexInput")
+        assert complex_input_type is not None
+        type_info = TypeInfo(test_schema, None, complex_input_type)
+
+        ast = parse_value('{ stringListField: ["foo"] }')
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(node: Node, *_args):
+                type_ = type_info.get_input_type()
+                visited.append(
+                    (
+                        "enter",
+                        node.kind,
+                        node.value if isinstance(node, NameNode) else None,
+                        str(type_),
+                    )
+                )
+
+            @staticmethod
+            def leave(node: Node, *_args):
+                type_ = type_info.get_input_type()
+                visited.append(
+                    (
+                        "leave",
+                        node.kind,
+                        node.value if isinstance(node, NameNode) else None,
+                        str(type_),
+                    )
+                )
+
+        visit(ast, TypeInfoVisitor(type_info, TestVisitor()))
+
+        assert visited == [
+            ("enter", "object_value", None, "ComplexInput"),
+            ("enter", "object_field", None, "[String]"),
+            ("enter", "name", "stringListField", "[String]"),
+            ("leave", "name", "stringListField", "[String]"),
+            ("enter", "list_value", None, "String"),
+            ("enter", "string_value", None, "String"),
+            ("leave", "string_value", None, "String"),
+            ("leave", "list_value", None, "String"),
+            ("leave", "object_field", None, "[String]"),
+            ("leave", "object_value", None, "ComplexInput"),
+        ]
+
+    def supports_traversal_of_selection_sets():
+        visited = []
+
+        human_type = test_schema.get_type("Human")
+        assert human_type is not None
+        type_info = TypeInfo(test_schema, None, human_type)
+
+        ast = parse("{ name, pets { name } }")
+        operation_node = ast.definitions[0]
+        assert isinstance(operation_node, OperationDefinitionNode)
+
+        class TestVisitor(Visitor):
+            @staticmethod
+            def enter(node: Node, *_args):
+                parent_type = type_info.get_parent_type()
+                type_ = type_info.get_type()
+                visited.append(
+                    (
+                        "enter",
+                        node.kind,
+                        node.value if isinstance(node, NameNode) else None,
+                        str(parent_type),
+                        str(type_),
+                    )
+                )
+
+            @staticmethod
+            def leave(node: Node, *_args):
+                parent_type = type_info.get_parent_type()
+                type_ = type_info.get_type()
+                visited.append(
+                    (
+                        "leave",
+                        node.kind,
+                        node.value if isinstance(node, NameNode) else None,
+                        str(parent_type),
+                        str(type_),
+                    )
+                )
+
+        visit(operation_node.selection_set, TypeInfoVisitor(type_info, TestVisitor()))
+
+        assert visited == [
+            ("enter", "selection_set", None, "Human", "Human"),
+            ("enter", "field", None, "Human", "String"),
+            ("enter", "name", "name", "Human", "String"),
+            ("leave", "name", "name", "Human", "String"),
+            ("leave", "field", None, "Human", "String"),
+            ("enter", "field", None, "Human", "[Pet]"),
+            ("enter", "name", "pets", "Human", "[Pet]"),
+            ("leave", "name", "pets", "Human", "[Pet]"),
+            ("enter", "selection_set", None, "Pet", "[Pet]"),
+            ("enter", "field", None, "Pet", "String"),
+            ("enter", "name", "name", "Pet", "String"),
+            ("leave", "name", "name", "Pet", "String"),
+            ("leave", "field", None, "Pet", "String"),
+            ("leave", "selection_set", None, "Pet", "[Pet]"),
+            ("leave", "field", None, "Human", "[Pet]"),
+            ("leave", "selection_set", None, "Human", "Human"),
+        ]
diff --git a/tests/utilities/test_value_from_ast.py b/tests/utilities/test_value_from_ast.py
new file mode 100644
index 0000000..c0ae631
--- /dev/null
+++ b/tests/utilities/test_value_from_ast.py
@@ -0,0 +1,240 @@
+from math import isnan, nan
+
+from graphql.language import parse_value, ValueNode
+from graphql.pyutils import Undefined
+from graphql.type import (
+    GraphQLBoolean,
+    GraphQLEnumType,
+    GraphQLFloat,
+    GraphQLID,
+    GraphQLInputField,
+    GraphQLInputObjectType,
+    GraphQLInt,
+    GraphQLList,
+    GraphQLNonNull,
+    GraphQLScalarType,
+    GraphQLString,
+)
+from graphql.utilities import value_from_ast
+
+
+def describe_value_from_ast():
+    def _value_from(value_text, type_, variables=None):
+        ast = parse_value(value_text)
+        return value_from_ast(ast, type_, variables)
+
+    def rejects_empty_input():
+        # noinspection PyTypeChecker
+        assert value_from_ast(None, GraphQLBoolean) is Undefined
+
+    def converts_according_to_input_coercion_rules():
+        assert _value_from("true", GraphQLBoolean) is True
+        assert _value_from("false", GraphQLBoolean) is False
+        assert _value_from("123", GraphQLInt) == 123
+        assert _value_from("123", GraphQLFloat) == 123
+        assert _value_from("123.456", GraphQLFloat) == 123.456
+        assert _value_from('"abc123"', GraphQLString) == "abc123"
+        assert _value_from("123456", GraphQLID) == "123456"
+        assert _value_from('"123456"', GraphQLID) == "123456"
+
+    def does_not_convert_when_input_coercion_rules_reject_a_value():
+        assert _value_from("123", GraphQLBoolean) is Undefined
+        assert _value_from("123.456", GraphQLInt) is Undefined
+        assert _value_from("true", GraphQLInt) is Undefined
+        assert _value_from('"123"', GraphQLInt) is Undefined
+        assert _value_from('"123"', GraphQLFloat) is Undefined
+        assert _value_from("123", GraphQLString) is Undefined
+        assert _value_from("true", GraphQLString) is Undefined
+        assert _value_from("123.456", GraphQLID) is Undefined
+
+    def convert_using_parse_literal_from_a_custom_scalar_type():
+        def pass_through_parse_literal(node, _vars=None):
+            assert node.kind == "string_value"
+            return node.value
+
+        pass_through_scalar = GraphQLScalarType(
+            "PassThroughScalar",
+            parse_literal=pass_through_parse_literal,
+            parse_value=lambda value: value,  # pragma: no cover
+        )
+
+        assert _value_from('"value"', pass_through_scalar) == "value"
+
+        def throw_parse_literal(_node: ValueNode, _vars=None):
+            raise RuntimeError("Test")
+
+        throw_scalar = GraphQLScalarType(
+            "ThrowScalar",
+            parse_literal=throw_parse_literal,
+            parse_value=lambda value: value,  # pragma: no cover
+        )
+
+        assert _value_from("value", throw_scalar) is Undefined
+
+        def undefined_parse_literal(_node: ValueNode, _vars=None):
+            return Undefined
+
+        return_undefined_scalar = GraphQLScalarType(
+            "ReturnUndefinedScalar",
+            parse_literal=undefined_parse_literal,
+            parse_value=lambda value: value,  # pragma: no cover
+        )
+
+        assert _value_from("value", return_undefined_scalar) is Undefined
+
+    def converts_enum_values_according_to_input_coercion_rules():
+        test_enum = GraphQLEnumType(
+            "TestColor",
+            {
+                "RED": 1,
+                "GREEN": 2,
+                "BLUE": 3,
+                "NULL": None,
+                "NAN": nan,
+                "NO_CUSTOM_VALUE": Undefined,
+            },
+        )
+
+        assert _value_from("RED", test_enum) == 1
+        assert _value_from("BLUE", test_enum) == 3
+        assert _value_from("YELLOW", test_enum) is Undefined
+        assert _value_from("3", test_enum) is Undefined
+        assert _value_from('"BLUE"', test_enum) is Undefined
+        assert _value_from("null", test_enum) is None
+        assert _value_from("NULL", test_enum) is None
+        assert _value_from("NULL", GraphQLNonNull(test_enum)) is None
+        assert isnan(_value_from("NAN", test_enum))
+        assert _value_from("NO_CUSTOM_VALUE", test_enum) is Undefined
+
+    # Boolean!
+    non_null_bool = GraphQLNonNull(GraphQLBoolean)
+    # [Boolean]
+    list_of_bool = GraphQLList(GraphQLBoolean)
+    # [Boolean!]
+    list_of_non_null_bool = GraphQLList(non_null_bool)
+    # [Boolean]!
+    non_null_list_of_bool = GraphQLNonNull(list_of_bool)
+    # [Boolean!]!
+    non_null_list_of_non_mull_bool = GraphQLNonNull(list_of_non_null_bool)
+
+    def coerces_to_null_unless_non_null():
+        assert _value_from("null", GraphQLBoolean) is None
+        assert _value_from("null", non_null_bool) is Undefined
+
+    def coerces_lists_of_values():
+        assert _value_from("true", list_of_bool) == [True]
+        assert _value_from("123", list_of_bool) is Undefined
+        assert _value_from("null", list_of_bool) is None
+        assert _value_from("[true, false]", list_of_bool) == [True, False]
+        assert _value_from("[true, 123]", list_of_bool) is Undefined
+        assert _value_from("[true, null]", list_of_bool) == [True, None]
+        assert _value_from("{ true: true }", list_of_bool) is Undefined
+
+    def coerces_non_null_lists_of_values():
+        assert _value_from("true", non_null_list_of_bool) == [True]
+        assert _value_from("123", non_null_list_of_bool) is Undefined
+        assert _value_from("null", non_null_list_of_bool) is Undefined
+        assert _value_from("[true, false]", non_null_list_of_bool) == [True, False]
+        assert _value_from("[true, 123]", non_null_list_of_bool) is Undefined
+        assert _value_from("[true, null]", non_null_list_of_bool) == [True, None]
+
+    def coerces_lists_of_non_null_values():
+        assert _value_from("true", list_of_non_null_bool) == [True]
+        assert _value_from("123", list_of_non_null_bool) is Undefined
+        assert _value_from("null", list_of_non_null_bool) is None
+        assert _value_from("[true, false]", list_of_non_null_bool) == [True, False]
+        assert _value_from("[true, 123]", list_of_non_null_bool) is Undefined
+        assert _value_from("[true, null]", list_of_non_null_bool) is Undefined
+
+    def coerces_non_null_lists_of_non_null_values():
+        assert _value_from("true", non_null_list_of_non_mull_bool) == [True]
+        assert _value_from("123", non_null_list_of_non_mull_bool) is Undefined
+        assert _value_from("null", non_null_list_of_non_mull_bool) is Undefined
+        assert _value_from("[true, false]", non_null_list_of_non_mull_bool) == [
+            True,
+            False,
+        ]
+        assert _value_from("[true, 123]", non_null_list_of_non_mull_bool) is Undefined
+        assert _value_from("[true, null]", non_null_list_of_non_mull_bool) is Undefined
+
+    test_input_obj = GraphQLInputObjectType(
+        "TestInput",
+        {
+            "int": GraphQLInputField(GraphQLInt, default_value=42),
+            "bool": GraphQLInputField(GraphQLBoolean),
+            "requiredBool": GraphQLInputField(non_null_bool),
+        },
+    )
+
+    def coerces_input_objects_according_to_input_coercion_rules():
+        assert _value_from("null", test_input_obj) is None
+        assert _value_from("[]", test_input_obj) is Undefined
+        assert _value_from("123", test_input_obj) is Undefined
+        assert _value_from("{ int: 123, requiredBool: false }", test_input_obj) == {
+            "int": 123,
+            "requiredBool": False,
+        }
+        assert _value_from("{ bool: true, requiredBool: false }", test_input_obj) == {
+            "int": 42,
+            "bool": True,
+            "requiredBool": False,
+        }
+        assert (
+            _value_from("{ int: true, requiredBool: true }", test_input_obj)
+            is Undefined
+        )
+        assert _value_from("{ requiredBool: null }", test_input_obj) is Undefined
+        assert _value_from("{ bool: true }", test_input_obj) is Undefined
+
+    def accepts_variable_values_assuming_already_coerced():
+        assert _value_from("$var", GraphQLBoolean, {}) is Undefined
+        assert _value_from("$var", GraphQLBoolean, {"var": True}) is True
+        assert _value_from("$var", GraphQLBoolean, {"var": None}) is None
+        assert _value_from("$var", non_null_bool, {"var": None}) is Undefined
+
+    def asserts_variables_are_provided_as_items_in_lists():
+        assert _value_from("[ $foo ]", list_of_bool, {}) == [None]
+        assert _value_from("[ $foo ]", list_of_non_null_bool, {}) is Undefined
+        assert _value_from("[ $foo ]", list_of_non_null_bool, {"foo": True}) == [True]
+        # Note: variables are expected to have already been coerced, so we
+        # do not expect the singleton wrapping behavior for variables.
+        assert _value_from("$foo", list_of_non_null_bool, {"foo": True}) is True
+        assert _value_from("$foo", list_of_non_null_bool, {"foo": [True]}) == [True]
+
+    def omits_input_object_fields_for_unprovided_variables():
+        assert _value_from(
+            "{ int: $foo, bool: $foo, requiredBool: true }", test_input_obj, {}
+        ) == {"int": 42, "requiredBool": True}
+        assert _value_from("{ requiredBool: $foo }", test_input_obj, {}) is Undefined
+        assert _value_from("{ requiredBool: $foo }", test_input_obj, {"foo": True}) == {
+            "int": 42,
+            "requiredBool": True,
+        }
+
+    def transforms_names_using_out_name():
+        # This is an extension of GraphQL.js.
+        complex_input_obj = GraphQLInputObjectType(
+            "Complex",
+            {
+                "realPart": GraphQLInputField(GraphQLFloat, out_name="real_part"),
+                "imagPart": GraphQLInputField(
+                    GraphQLFloat, default_value=0, out_name="imag_part"
+                ),
+            },
+        )
+        assert _value_from("{ realPart: 1 }", complex_input_obj) == {
+            "real_part": 1,
+            "imag_part": 0,
+        }
+
+    def transforms_values_with_out_type():
+        # This is an extension of GraphQL.js.
+        complex_input_obj = GraphQLInputObjectType(
+            "Complex",
+            {
+                "real": GraphQLInputField(GraphQLFloat),
+                "imag": GraphQLInputField(GraphQLFloat),
+            },
+            out_type=lambda value: complex(value["real"], value["imag"]),
+        )
+        assert _value_from("{ real: 1, imag: 2 }", complex_input_obj) == 1 + 2j
diff --git a/tests/utilities/test_value_from_ast_untyped.py b/tests/utilities/test_value_from_ast_untyped.py
new file mode 100644
index 0000000..076fc3d
--- /dev/null
+++ b/tests/utilities/test_value_from_ast_untyped.py
@@ -0,0 +1,64 @@
+from math import nan
+
+from graphql.language import parse_value, FloatValueNode, IntValueNode
+from graphql.pyutils import Undefined
+from graphql.utilities import value_from_ast_untyped
+
+
+def describe_value_from_ast_untyped():
+    def _compare_value(value, expected):
+        if expected is None:
+            assert value is None
+        elif expected is Undefined:
+            assert value is Undefined
+        elif expected is nan:
+            assert value is nan
+        else:
+            assert value == expected
+
+    def _test_case(value_text, expected):
+        value_node = parse_value(value_text)
+        _compare_value(value_from_ast_untyped(value_node), expected)
+
+    def _test_case_with_vars(value_text, variables, expected):
+        value_node = parse_value(value_text)
+        _compare_value(value_from_ast_untyped(value_node, variables), expected)
+
+    def parses_simple_values():
+        _test_case("null", None)
+        _test_case("true", True)
+        _test_case("false", False)
+        _test_case("123", 123)
+        _test_case("123.456", 123.456)
+        _test_case('"abc123"', "abc123")
+
+    def parses_lists_of_values():
+        _test_case("[true, false]", [True, False])
+        _test_case("[true, 123.45]", [True, 123.45])
+        _test_case("[true, null]", [True, None])
+        _test_case('[true, ["foo", 1.2]]', [True, ["foo", 1.2]])
+
+    def parses_input_objects():
+        _test_case("{ int: 123, bool: false }", {"int": 123, "bool": False})
+        _test_case('{ foo: [ { bar: "baz"} ] }', {"foo": [{"bar": "baz"}]})
+
+    def parses_enum_values_as_plain_strings():
+        _test_case("TEST_ENUM_VALUE", "TEST_ENUM_VALUE")
+        _test_case("[TEST_ENUM_VALUE]", ["TEST_ENUM_VALUE"])
+
+    def parses_variables():
+        _test_case_with_vars("$testVariable", {"testVariable": "foo"}, "foo")
+        _test_case_with_vars("[$testVariable]", {"testVariable": "foo"}, ["foo"])
+        _test_case_with_vars(
+            "{a:[$testVariable]}", {"testVariable": "foo"}, {"a": ["foo"]}
+        )
+        _test_case_with_vars("$testVariable", {"testVariable": None}, None)
+        _test_case_with_vars("$testVariable", {"testVariable": nan}, nan)
+        _test_case_with_vars("$testVariable", {}, Undefined)
+        _test_case_with_vars("$testVariable", None, Undefined)
+
+    def parse_invalid_int_as_nan():
+        assert value_from_ast_untyped(IntValueNode(value="invalid")) is nan
+
+    def parse_invalid_float_as_nan():
+        assert value_from_ast_untyped(FloatValueNode(value="invalid")) is nan
diff --git a/tests/utils/__init__.py b/tests/utils/__init__.py
new file mode 100644
index 0000000..a6e55a4
--- /dev/null
+++ b/tests/utils/__init__.py
@@ -0,0 +1,6 @@
+"""Test utilities"""
+
+from .dedent import dedent
+from .gen_fuzz_strings import gen_fuzz_strings
+
+__all__ = ["dedent", "gen_fuzz_strings"]
diff --git a/tests/utils/dedent.py b/tests/utils/dedent.py
new file mode 100644
index 0000000..6cd4fde
--- /dev/null
+++ b/tests/utils/dedent.py
@@ -0,0 +1,12 @@
+from textwrap import dedent as _dedent
+
+__all__ = ["dedent"]
+
+
+def dedent(text: str) -> str:
+    """Fix indentation of given text by removing leading spaces and tabs.
+
+    Also removes leading newlines and trailing spaces and tabs, but keeps trailing
+    newlines.
+    """
+    return _dedent(text.lstrip("\n").rstrip(" \t"))
diff --git a/tests/utils/gen_fuzz_strings.py b/tests/utils/gen_fuzz_strings.py
new file mode 100644
index 0000000..306984b
--- /dev/null
+++ b/tests/utils/gen_fuzz_strings.py
@@ -0,0 +1,10 @@
+from itertools import product
+from typing import Generator
+
+__all__ = ["gen_fuzz_strings"]
+
+
+def gen_fuzz_strings(allowed_chars: str, max_length: int) -> Generator[str, None, None]:
+    """Generator that produces all possible combinations of allowed characters."""
+    for length in range(max_length + 1):
+        yield from map("".join, product(allowed_chars, repeat=length))
diff --git a/tests/utils/test_dedent.py b/tests/utils/test_dedent.py
new file mode 100644
index 0000000..25bdfb9
--- /dev/null
+++ b/tests/utils/test_dedent.py
@@ -0,0 +1,107 @@
+from . import dedent
+
+
+def describe_dedent():
+    def removes_indentation_in_typical_usage():
+        assert (
+            dedent(
+                """
+                type Query {
+                  me: User
+                }
+
+                type User {
+                  id: ID
+                  name: String
+                }
+                """
+            )
+            == "type Query {\n  me: User\n}\n\n"
+            "type User {\n  id: ID\n  name: String\n}\n"
+        )
+
+    def removes_only_the_first_level_of_indentation():
+        assert (
+            dedent(
+                """
+                first
+                  second
+                    third
+                      fourth
+                """
+            )
+            == "first\n  second\n    third\n      fourth\n"
+        )
+
+    def does_not_escape_special_characters():
+        assert (
+            dedent(
+                """
+                type Root {
+                  field(arg: String = "wi\th de\fault"): String
+                }
+                """
+            )
+            == "type Root {\n"
+            '  field(arg: String = "wi\th de\fault"): String\n}\n'
+        )
+
+    def also_removes_indentation_using_tabs():
+        assert (
+            dedent(
+                """
+                \t\t    type Query {
+                \t\t      me: User
+                \t\t    }
+                """
+            )
+            == "type Query {\n  me: User\n}\n"
+        )
+
+    def removes_leading_newlines():
+        assert (
+            dedent(
+                """
+
+
+                 type Query {
+                   me: User
+                 }"""
+            )
+            == "type Query {\n  me: User\n}"
+        )
+
+    def does_not_remove_trailing_newlines():
+        assert (
+            dedent(
+                """
+                type Query {
+                  me: User
+                }
+
+                """
+            )
+            == "type Query {\n  me: User\n}\n\n"
+        )
+
+    def removes_all_trailing_spaces_and_tabs():
+        assert (
+            dedent(
+                """
+                type Query {
+                  me: User
+                }
+                    \t\t  \t """
+            )
+            == "type Query {\n  me: User\n}\n"
+        )
+
+    def works_on_text_without_leading_newline():
+        assert (
+            dedent(
+                """                type Query {
+                  me: User
+                }"""
+            )
+            == "type Query {\n  me: User\n}"
+        )
diff --git a/tests/utils/test_gen_fuzz_strings.py b/tests/utils/test_gen_fuzz_strings.py
new file mode 100644
index 0000000..147ccca
--- /dev/null
+++ b/tests/utils/test_gen_fuzz_strings.py
@@ -0,0 +1,59 @@
+from . import gen_fuzz_strings
+
+
+def describe_gen_fuzz_strings():
+    def always_provide_empty_string():
+        assert list(gen_fuzz_strings(allowed_chars="", max_length=0)) == [""]
+        assert list(gen_fuzz_strings(allowed_chars="", max_length=1)) == [""]
+        assert list(gen_fuzz_strings(allowed_chars="a", max_length=0)) == [""]
+
+    def generate_strings_with_single_character():
+        assert list(gen_fuzz_strings(allowed_chars="a", max_length=1)) == ["", "a"]
+        assert list(gen_fuzz_strings(allowed_chars="abc", max_length=1)) == [
+            "",
+            "a",
+            "b",
+            "c",
+        ]
+
+    def generate_strings_with_multiple_character():
+        assert list(gen_fuzz_strings(allowed_chars="a", max_length=2)) == [
+            "",
+            "a",
+            "aa",
+        ]
+
+        assert list(gen_fuzz_strings(allowed_chars="abc", max_length=2)) == [
+            "",
+            "a",
+            "b",
+            "c",
+            "aa",
+            "ab",
+            "ac",
+            "ba",
+            "bb",
+            "bc",
+            "ca",
+            "cb",
+            "cc",
+        ]
+
+    def generate_strings_longer_than_possible_number_of_characters():
+        assert list(gen_fuzz_strings(allowed_chars="ab", max_length=3)) == [
+            "",
+            "a",
+            "b",
+            "aa",
+            "ab",
+            "ba",
+            "bb",
+            "aaa",
+            "aab",
+            "aba",
+            "abb",
+            "baa",
+            "bab",
+            "bba",
+            "bbb",
+        ]
diff --git a/tests/validation/__init__.py b/tests/validation/__init__.py
new file mode 100644
index 0000000..e73a25f
--- /dev/null
+++ b/tests/validation/__init__.py
@@ -0,0 +1,5 @@
+"""Tests for graphql.validation"""
+
+from pytest import register_assert_rewrite  # type: ignore
+
+register_assert_rewrite("tests.validation.harness")
diff --git a/tests/validation/harness.py b/tests/validation/harness.py
new file mode 100644
index 0000000..0084435
--- /dev/null
+++ b/tests/validation/harness.py
@@ -0,0 +1,167 @@
+from typing import List, Optional, Type
+
+from graphql.error import GraphQLError
+from graphql.language import parse
+from graphql.type import GraphQLSchema
+from graphql.utilities import build_schema
+from graphql.validation import ValidationRule, SDLValidationRule
+from graphql.validation.validate import validate, validate_sdl
+
+test_schema = build_schema(
+    """
+    interface Being {
+      name(surname: Boolean): String
+    }
+
+    interface Mammal {
+      mother: Mammal
+      father: Mammal
+    }
+
+    interface Pet implements Being {
+      name(surname: Boolean): String
+    }
+
+    interface Canine implements Mammal & Being {
+      name(surname: Boolean): String
+      mother: Canine
+      father: Canine
+    }
+
+    enum DogCommand {
+      SIT
+      HEEL
+      DOWN
+    }
+
+    type Dog implements Being & Pet & Mammal & Canine {
+      name(surname: Boolean): String
+      nickname: String
+      barkVolume: Int
+      barks: Boolean
+      doesKnowCommand(dogCommand: DogCommand): Boolean
+      isHouseTrained(atOtherHomes: Boolean = true): Boolean
+      isAtLocation(x: Int, y: Int): Boolean
+      mother: Dog
+      father: Dog
+    }
+
+    type Cat implements Being & Pet {
+      name(surname: Boolean): String
+      nickname: String
+      meows: Boolean
+      meowsVolume: Int
+      furColor: FurColor
+    }
+
+    union CatOrDog = Cat | Dog
+
+    interface Intelligent {
+      iq: Int
+    }
+
+    type Human implements Being & Intelligent {
+      name(surname: Boolean): String
+      pets: [Pet]
+      relatives: [Human]
+      iq: Int
+    }
+
+    type Alien implements Being & Intelligent {
+      name(surname: Boolean): String
+      numEyes: Int
+      iq: Int
+    }
+
+    union DogOrHuman = Dog | Human
+
+    union HumanOrAlien = Human | Alien
+
+    enum FurColor {
+      BROWN
+      BLACK
+      TAN
+      SPOTTED
+      NO_FUR
+      UNKNOWN
+    }
+
+    input ComplexInput {
+      requiredField: Boolean!
+      nonNullField: Boolean! = false
+      intField: Int
+      stringField: String
+      booleanField: Boolean
+      stringListField: [String]
+    }
+
+    type ComplicatedArgs {
+      # TODO List
+      # TODO Coercion
+      # TODO NotNulls
+      intArgField(intArg: Int): String
+      nonNullIntArgField(nonNullIntArg: Int!): String
+      stringArgField(stringArg: String): String
+      booleanArgField(booleanArg: Boolean): String
+      enumArgField(enumArg: FurColor): String
+      floatArgField(floatArg: Float): String
+      idArgField(idArg: ID): String
+      stringListArgField(stringListArg: [String]): String
+      stringListNonNullArgField(stringListNonNullArg: [String!]): String
+      complexArgField(complexArg: ComplexInput): String
+      multipleReqs(req1: Int!, req2: Int!): String
+      nonNullFieldWithDefault(arg: Int! = 0): String
+      multipleOpts(opt1: Int = 0, opt2: Int = 0): String
+      multipleOptAndReq(req1: Int!, req2: Int!, opt1: Int = 0, opt2: Int = 0): String
+    }
+
+    type QueryRoot {
+      human(id: ID): Human
+      alien: Alien
+      dog: Dog
+      cat: Cat
+      pet: Pet
+      catOrDog: CatOrDog
+      dogOrHuman: DogOrHuman
+      humanOrAlien: HumanOrAlien
+      complicatedArgs: ComplicatedArgs
+    }
+
+    schema {
+      query: QueryRoot
+    }
+
+    directive @onQuery on QUERY
+    directive @onMutation on MUTATION
+    directive @onSubscription on SUBSCRIPTION
+    directive @onField on FIELD
+    directive @onFragmentDefinition on FRAGMENT_DEFINITION
+    directive @onFragmentSpread on FRAGMENT_SPREAD
+    directive @onInlineFragment on INLINE_FRAGMENT
+    directive @onVariableDefinition on VARIABLE_DEFINITION
+    """
+)
+
+
+def assert_validation_errors(
+    rule: Type[ValidationRule],
+    query_str: str,
+    errors: List[GraphQLError],
+    schema: GraphQLSchema = test_schema,
+) -> List[GraphQLError]:
+    doc = parse(query_str)
+    returned_errors = validate(schema, doc, [rule])
+    assert returned_errors == errors
+    return returned_errors
+
+
+def assert_sdl_validation_errors(
+    rule: Type[SDLValidationRule],
+    sdl_str: str,
+    errors: List[GraphQLError],
+    schema: Optional[GraphQLSchema] = None,
+) -> List[GraphQLError]:
+    doc = parse(sdl_str)
+    returned_errors = validate_sdl(doc, schema, [rule])
+    assert returned_errors == errors
+    return returned_errors
diff --git a/tests/validation/test_executable_definitions.py b/tests/validation/test_executable_definitions.py
new file mode 100644
index 0000000..4a21c63
--- /dev/null
+++ b/tests/validation/test_executable_definitions.py
@@ -0,0 +1,96 @@
+from functools import partial
+
+from graphql.validation import ExecutableDefinitionsRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, ExecutableDefinitionsRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_executable_definitions():
+    def with_only_operation():
+        assert_valid(
+            """
+            query Foo {
+              dog {
+                name
+              }
+            }
+            """
+        )
+
+    def with_operation_and_fragment():
+        assert_valid(
+            """
+            query Foo {
+              dog {
+                name
+                ...Frag
+              }
+            }
+
+            fragment Frag on Dog {
+              name
+            }
+            """
+        )
+
+    def with_type_definition():
+        assert_errors(
+            """
+            query Foo {
+              dog {
+                name
+              }
+            }
+
+            type Cow {
+              name: String
+            }
+
+            extend type Dog {
+              color: String
+            }
+            """,
+            [
+                {
+                    "message": "The 'Cow' definition is not executable.",
+                    "locations": [(8, 13)],
+                },
+                {
+                    "message": "The 'Dog' definition is not executable.",
+                    "locations": [(12, 13)],
+                },
+            ],
+        )
+
+    def with_schema_definition():
+        assert_errors(
+            """
+            schema {
+              query: Query
+            }
+
+            type Query {
+              test: String
+            }
+
+            extend schema @directive
+            """,
+            [
+                {
+                    "message": "The schema definition is not executable.",
+                    "locations": [(2, 13)],
+                },
+                {
+                    "message": "The 'Query' definition is not executable.",
+                    "locations": [(6, 13)],
+                },
+                {
+                    "message": "The schema definition is not executable.",
+                    "locations": [(10, 13)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_fields_on_correct_type.py b/tests/validation/test_fields_on_correct_type.py
new file mode 100644
index 0000000..dfd53c2
--- /dev/null
+++ b/tests/validation/test_fields_on_correct_type.py
@@ -0,0 +1,421 @@
+from functools import partial
+
+from graphql.language import parse
+from graphql.type import GraphQLSchema
+from graphql.utilities import build_schema
+from graphql.validation import validate, FieldsOnCorrectTypeRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, FieldsOnCorrectTypeRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_fields_on_correct_type():
+    def object_field_selection():
+        assert_valid(
+            """
+            fragment objectFieldSelection on Dog {
+              __typename
+              name
+            }
+            """
+        )
+
+    def aliased_object_field_selection():
+        assert_valid(
+            """
+            fragment aliasedObjectFieldSelection on Dog {
+              tn : __typename
+              otherName : name
+            }
+            """
+        )
+
+    def interface_field_selection():
+        assert_valid(
+            """
+            fragment interfaceFieldSelection on Pet {
+              __typename
+              name
+            }
+            """
+        )
+
+    def aliased_interface_field_selection():
+        assert_valid(
+            """
+            fragment interfaceFieldSelection on Pet {
+              otherName : name
+            }
+            """
+        )
+
+    def lying_alias_selection():
+        assert_valid(
+            """
+            fragment lyingAliasSelection on Dog {
+              name : nickname
+            }
+            """
+        )
+
+    def ignores_fields_on_unknown_type():
+        assert_valid(
+            """
+            fragment unknownSelection on UnknownType {
+              unknownField
+            }
+            """
+        )
+
+    def reports_errors_when_type_is_known_again():
+        assert_errors(
+            """
+            fragment typeKnownAgain on Pet {
+              unknown_pet_field {
+                ... on Cat {
+                  unknown_cat_field
+                }
+              }
+            },
+            """,
+            [
+                {
+                    "message": "Cannot query field 'unknown_pet_field' on type 'Pet'.",
+                    "locations": [(3, 15)],
+                },
+                {
+                    "message": "Cannot query field 'unknown_cat_field' on type 'Cat'.",
+                    "locations": [(5, 19)],
+                },
+            ],
+        )
+
+    def field_not_defined_on_fragment():
+        assert_errors(
+            """
+            fragment fieldNotDefined on Dog {
+              meowVolume
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'meowVolume' on type 'Dog'."
+                    " Did you mean 'barkVolume'?",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def ignores_deeply_unknown_field():
+        assert_errors(
+            """
+            fragment deepFieldNotDefined on Dog {
+              unknown_field {
+                deeper_unknown_field
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'unknown_field' on type 'Dog'.",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def sub_field_not_defined():
+        assert_errors(
+            """
+            fragment subFieldNotDefined on Human {
+              pets {
+                unknown_field
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'unknown_field' on type 'Pet'.",
+                    "locations": [(4, 17)],
+                },
+            ],
+        )
+
+    def field_not_defined_on_inline_fragment():
+        assert_errors(
+            """
+            fragment fieldNotDefined on Pet {
+              ... on Dog {
+                meowVolume
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'meowVolume' on type 'Dog'."
+                    " Did you mean 'barkVolume'?",
+                    "locations": [(4, 17)],
+                },
+            ],
+        )
+
+    def aliased_field_target_not_defined():
+        assert_errors(
+            """
+            fragment aliasedFieldTargetNotDefined on Dog {
+              volume : mooVolume
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'mooVolume' on type 'Dog'."
+                    " Did you mean 'barkVolume'?",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def aliased_lying_field_target_not_defined():
+        assert_errors(
+            """
+            fragment aliasedLyingFieldTargetNotDefined on Dog {
+              barkVolume : kawVolume
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'kawVolume' on type 'Dog'."
+                    " Did you mean 'barkVolume'?",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def not_defined_on_interface():
+        assert_errors(
+            """
+            fragment notDefinedOnInterface on Pet {
+              tailLength
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'tailLength' on type 'Pet'.",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def defined_on_implementors_but_not_on_interface():
+        assert_errors(
+            """
+            fragment definedOnImplementorsButNotInterface on Pet {
+              nickname
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'nickname' on type 'Pet'."
+                    " Did you mean to use an inline fragment on 'Cat' or 'Dog'?",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def meta_field_selection_on_union():
+        assert_valid(
+            """
+            fragment directFieldSelectionOnUnion on CatOrDog {
+              __typename
+            }
+            """
+        )
+
+    def direct_field_selection_on_union():
+        assert_errors(
+            """
+            fragment directFieldSelectionOnUnion on CatOrDog {
+              directField
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'directField' on type 'CatOrDog'.",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def defined_on_implementors_queried_on_union():
+        assert_errors(
+            """
+              fragment definedOnImplementorsQueriedOnUnion on CatOrDog {
+              name
+            }
+            """,
+            [
+                {
+                    "message": "Cannot query field 'name' on type 'CatOrDog'."
+                    " Did you mean to use an inline fragment"
+                    " on 'Being', 'Pet', 'Canine', 'Cat', or 'Dog'?",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def valid_field_in_inline_fragment():
+        assert_valid(
+            """
+            fragment objectFieldSelection on Pet {
+              ... on Dog {
+                name
+              }
+              ... {
+                name
+              }
+            }
+            """
+        )
+
+
+def describe_fields_on_correct_type_error_message():
+    def _error_message(schema: GraphQLSchema, query_str: str):
+        errors = validate(schema, parse(query_str), [FieldsOnCorrectTypeRule])
+        assert len(errors) == 1
+        return errors[0].message
+
+    def fields_correct_type_no_suggestion():
+        schema = build_schema(
+            """
+            type T {
+              fieldWithVeryLongNameThatWillNeverBeSuggested: String
+            }
+            type Query { t: T }
+            """
+        )
+        assert _error_message(schema, "{ t { f } }") == (
+            "Cannot query field 'f' on type 'T'."
+        )
+
+    def works_with_no_small_numbers_of_type_suggestion():
+        schema = build_schema(
+            """
+            union T = A | B
+            type Query { t: T }
+
+            type A { f: String }
+            type B { f: String }
+            """
+        )
+        assert _error_message(schema, "{ t { f } }") == (
+            "Cannot query field 'f' on type 'T'."
+            " Did you mean to use an inline fragment on 'A' or 'B'?"
+        )
+
+    def works_with_no_small_numbers_of_field_suggestion():
+        schema = build_schema(
+            """
+            type T {
+              y: String
+              z: String
+            }
+            type Query { t: T }
+            """
+        )
+        assert _error_message(schema, "{ t { f } }") == (
+            "Cannot query field 'f' on type 'T'. Did you mean 'y' or 'z'?"
+        )
+
+    def only_shows_one_set_of_suggestions_at_a_time_preferring_types():
+        schema = build_schema(
+            """
+            interface T {
+              y: String
+              z: String
+            }
+            type Query { t: T }
+
+            type A implements T {
+              f: String
+              y: String
+              z: String
+            }
+            type B implements T {
+              f: String
+              y: String
+              z: String
+            }
+            """
+        )
+        assert _error_message(schema, "{ t { f } }") == (
+            "Cannot query field 'f' on type 'T'."
+            " Did you mean to use an inline fragment on 'A' or 'B'?"
+        )
+
+    def sort_type_suggestions_based_on_inheritance_order():
+        schema = build_schema(
+            """
+            interface T { bar: String }
+            type Query { t: T }
+
+            interface Z implements T {
+              foo: String
+              bar: String
+            }
+
+            interface Y implements Z & T {
+              foo: String
+              bar: String
+            }
+
+            type X implements Y & Z & T {
+              foo: String
+              bar: String
+            }
+            """
+        )
+
+        assert _error_message(schema, "{ t { foo } }") == (
+            "Cannot query field 'foo' on type 'T'."
+            " Did you mean to use an inline fragment on 'Z', 'Y', or 'X'?"
+        )
+
+    def limits_lots_of_type_suggestions():
+        schema = build_schema(
+            """
+            union T = A | B | C | D | E | F
+            type Query { t: T }
+
+            type A { f: String }
+            type B { f: String }
+            type C { f: String }
+            type D { f: String }
+            type E { f: String }
+            type F { f: String }
+            """
+        )
+        assert _error_message(schema, "{ t { f } }") == (
+            "Cannot query field 'f' on type 'T'. Did you mean to use"
+            " an inline fragment on 'A', 'B', 'C', 'D', or 'E'?"
+        )
+
+    def limits_lots_of_field_suggestions():
+        schema = build_schema(
+            """
+            type T {
+              u: String
+              v: String
+              w: String
+              x: String
+              y: String
+              z: String
+            }
+            type Query { t: T }
+            """
+        )
+        assert _error_message(schema, "{ t { f } }") == (
+            "Cannot query field 'f' on type 'T'."
+            " Did you mean 'u', 'v', 'w', 'x', or 'y'?"
+        )
diff --git a/tests/validation/test_fragments_on_composite_types.py b/tests/validation/test_fragments_on_composite_types.py
new file mode 100644
index 0000000..a495766
--- /dev/null
+++ b/tests/validation/test_fragments_on_composite_types.py
@@ -0,0 +1,137 @@
+from functools import partial
+
+from graphql.validation import FragmentsOnCompositeTypesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, FragmentsOnCompositeTypesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_fragments_on_composite_types():
+    def object_is_valid_fragment_type():
+        assert_valid(
+            """
+            fragment validFragment on Dog {
+              barks
+            }
+            """
+        )
+
+    def interface_is_valid_fragment_type():
+        assert_valid(
+            """
+            fragment validFragment on Pet {
+              name
+            }
+            """
+        )
+
+    def object_is_valid_inline_fragment_type():
+        assert_valid(
+            """
+            fragment validFragment on Pet {
+              ... on Dog {
+                barks
+              }
+            }
+            """
+        )
+
+    def interface_is_valid_inline_fragment_type():
+        assert_valid(
+            """
+            fragment validFragment on Mammal {
+              ... on Canine {
+                name
+              }
+            }
+            """
+        )
+
+    def inline_fragment_without_type_is_valid():
+        assert_valid(
+            """
+            fragment validFragment on Pet {
+              ... {
+                name
+              }
+            }
+            """
+        )
+
+    def union_is_valid_fragment_type():
+        assert_valid(
+            """
+            fragment validFragment on CatOrDog {
+              __typename
+            }
+            """
+        )
+
+    def scalar_is_invalid_fragment_type():
+        assert_errors(
+            """
+            fragment scalarFragment on Boolean {
+              bad
+            }
+            """,
+            [
+                {
+                    "message": "Fragment 'scalarFragment' cannot condition"
+                    " on non composite type 'Boolean'.",
+                    "locations": [(2, 40)],
+                },
+            ],
+        )
+
+    def enum_is_invalid_fragment_type():
+        assert_errors(
+            """
+            fragment scalarFragment on FurColor {
+              bad
+            }
+            """,
+            [
+                {
+                    "message": "Fragment 'scalarFragment' cannot condition"
+                    " on non composite type 'FurColor'.",
+                    "locations": [(2, 40)],
+                },
+            ],
+        )
+
+    def input_object_is_invalid_fragment_type():
+        assert_errors(
+            """
+            fragment inputFragment on ComplexInput {
+              stringField
+            }
+            """,
+            [
+                {
+                    "message": "Fragment 'inputFragment' cannot condition"
+                    " on non composite type 'ComplexInput'.",
+                    "locations": [(2, 39)],
+                },
+            ],
+        )
+
+    def scalar_is_invalid_inline_fragment_type():
+        assert_errors(
+            """
+            fragment invalidFragment on Pet {
+              ... on String {
+                barks
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Fragment cannot condition"
+                    " on non composite type 'String'.",
+                    "locations": [(3, 22)],
+                }
+            ],
+        )
diff --git a/tests/validation/test_known_argument_names.py b/tests/validation/test_known_argument_names.py
new file mode 100644
index 0000000..3291b7c
--- /dev/null
+++ b/tests/validation/test_known_argument_names.py
@@ -0,0 +1,358 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation import KnownArgumentNamesRule
+from graphql.validation.rules.known_argument_names import (
+    KnownArgumentNamesOnDirectivesRule,
+)
+
+from .harness import assert_validation_errors, assert_sdl_validation_errors
+
+assert_errors = partial(assert_validation_errors, KnownArgumentNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+assert_sdl_errors = partial(
+    assert_sdl_validation_errors, KnownArgumentNamesOnDirectivesRule
+)
+
+assert_sdl_valid = partial(assert_sdl_errors, errors=[])
+
+
+def describe_validate_known_argument_names():
+    def single_arg_is_known():
+        assert_valid(
+            """
+            fragment argOnRequiredArg on Dog {
+              doesKnowCommand(dogCommand: SIT)
+            }
+            """
+        )
+
+    def multiple_args_are_known():
+        assert_valid(
+            """
+            fragment multipleArgs on ComplicatedArgs {
+              multipleReqs(req1: 1, req2: 2)
+            }
+            """
+        )
+
+    def ignore_args_of_unknown_fields():
+        assert_valid(
+            """
+            fragment argOnUnknownField on Dog {
+              unknownField(unknownArg: SIT)
+            }
+            """
+        )
+
+    def multiple_args_in_reverse_order_are_known():
+        assert_valid(
+            """
+            fragment multipleArgsReverseOrder on ComplicatedArgs {
+              multipleReqs(req2: 2, req1: 1)
+            }
+            """
+        )
+
+    def no_args_on_optional_arg():
+        assert_valid(
+            """
+            fragment noArgOnOptionalArg on Dog {
+              isHouseTrained
+            }
+            """
+        )
+
+    def args_are_known_deeply():
+        assert_valid(
+            """
+            {
+              dog {
+                doesKnowCommand(dogCommand: SIT)
+              }
+              human {
+                pet {
+                  ... on Dog {
+                      doesKnowCommand(dogCommand: SIT)
+                  }
+                }
+              }
+            }
+            """
+        )
+
+    def directive_args_are_known():
+        assert_valid(
+            """
+            {
+              dog @skip(if: true)
+            }
+            """
+        )
+
+    def field_args_are_invalid():
+        assert_errors(
+            """
+            {
+              dog @skip(unless: true)
+            }
+            """,
+            [
+                {
+                    "message": "Unknown argument 'unless' on directive '@skip'.",
+                    "locations": [(3, 25)],
+                }
+            ],
+        )
+
+    def directive_without_args_is_valid():
+        assert_valid(
+            """
+            {
+                dog @onField
+            }
+            """
+        )
+
+    def arg_passed_to_directive_without_args_is_reported():
+        assert_errors(
+            """
+            {
+                dog @onField(if: true)
+            }
+            """,
+            [
+                {
+                    "message": "Unknown argument 'if' on directive '@onField'.",
+                    "locations": [(3, 30)],
+                }
+            ],
+        )
+
+    def misspelled_directive_args_are_reported():
+        assert_errors(
+            """
+            {
+              dog @skip(iff: true)
+            }
+            """,
+            [
+                {
+                    "message": "Unknown argument 'iff' on directive '@skip'."
+                    " Did you mean 'if'?",
+                    "locations": [(3, 25)],
+                }
+            ],
+        )
+
+    def invalid_arg_name():
+        assert_errors(
+            """
+            fragment invalidArgName on Dog {
+              doesKnowCommand(unknown: true)
+            }
+            """,
+            [
+                {
+                    "message": "Unknown argument 'unknown'"
+                    " on field 'Dog.doesKnowCommand'.",
+                    "locations": [(3, 31)],
+                },
+            ],
+        )
+
+    def misspelled_arg_name_is_reported():
+        assert_errors(
+            """
+            fragment invalidArgName on Dog {
+              doesKnowCommand(DogCommand: true)
+            }
+            """,
+            [
+                {
+                    "message": "Unknown argument 'DogCommand'"
+                    " on field 'Dog.doesKnowCommand'."
+                    " Did you mean 'dogCommand'?",
+                    "locations": [(3, 31)],
+                }
+            ],
+        )
+
+    def unknown_args_amongst_known_args():
+        assert_errors(
+            """
+            fragment oneGoodArgOneInvalidArg on Dog {
+              doesKnowCommand(whoKnows: 1, dogCommand: SIT, unknown: true)
+            }
+            """,
+            [
+                {
+                    "message": "Unknown argument 'whoKnows'"
+                    " on field 'Dog.doesKnowCommand'.",
+                    "locations": [(3, 31)],
+                },
+                {
+                    "message": "Unknown argument 'unknown'"
+                    " on field 'Dog.doesKnowCommand'.",
+                    "locations": [(3, 61)],
+                },
+            ],
+        )
+
+    def unknown_args_deeply():
+        assert_errors(
+            """
+            {
+              dog {
+                doesKnowCommand(unknown: true)
+              }
+              human {
+                pet {
+                  ... on Dog {
+                    doesKnowCommand(unknown: true)
+                  }
+                }
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Unknown argument 'unknown'"
+                    " on field 'Dog.doesKnowCommand'.",
+                    "locations": [(4, 33)],
+                },
+                {
+                    "message": "Unknown argument 'unknown'"
+                    " on field 'Dog.doesKnowCommand'.",
+                    "locations": [(9, 37)],
+                },
+            ],
+        )
+
+    def describe_within_sdl():
+        def known_arg_on_directive_inside_sdl():
+            assert_sdl_valid(
+                """
+                type Query {
+                  foo: String @test(arg: "")
+                }
+
+                directive @test(arg: String) on FIELD_DEFINITION
+                """
+            )
+
+        def unknown_arg_on_directive_defined_inside_sdl():
+            assert_sdl_errors(
+                """
+                type Query {
+                  foo: String @test(unknown: "")
+                }
+
+                directive @test(arg: String) on FIELD_DEFINITION
+                """,
+                [
+                    {
+                        "message": "Unknown argument 'unknown' on directive '@test'.",
+                        "locations": [(3, 37)],
+                    },
+                ],
+            )
+
+        def misspelled_arg_name_is_reported_on_directive_defined_inside_sdl():
+            assert_sdl_errors(
+                """
+                type Query {
+                  foo: String @test(agr: "")
+                }
+
+                directive @test(arg: String) on FIELD_DEFINITION
+                """,
+                [
+                    {
+                        "message": "Unknown argument 'agr' on directive '@test'."
+                        " Did you mean 'arg'?",
+                        "locations": [(3, 37)],
+                    },
+                ],
+            )
+
+        def unknown_arg_on_standard_directive():
+            assert_sdl_errors(
+                """
+                type Query {
+                  foo: String @deprecated(unknown: "")
+                }
+                """,
+                [
+                    {
+                        "message": "Unknown argument 'unknown'"
+                        " on directive '@deprecated'.",
+                        "locations": [(3, 43)],
+                    },
+                ],
+            )
+
+        def unknown_arg_on_overridden_standard_directive():
+            assert_sdl_errors(
+                """
+                type Query {
+                  foo: String @deprecated(reason: "")
+                }
+                directive @deprecated(arg: String) on FIELD
+                """,
+                [
+                    {
+                        "message": "Unknown argument 'reason'"
+                        " on directive '@deprecated'.",
+                        "locations": [(3, 43)],
+                    },
+                ],
+            )
+
+        def unknown_arg_on_directive_defined_in_schema_extension():
+            schema = build_schema(
+                """
+                type Query {
+                  foo: String
+                }
+                """
+            )
+            assert_sdl_errors(
+                """
+                directive @test(arg: String) on OBJECT
+
+                extend type Query  @test(unknown: "")
+                """,
+                [
+                    {
+                        "message": "Unknown argument 'unknown' on directive '@test'.",
+                        "locations": [(4, 42)],
+                    },
+                ],
+                schema,
+            )
+
+        def unknown_arg_on_directive_used_in_schema_extension():
+            schema = build_schema(
+                """
+                directive @test(arg: String) on OBJECT
+
+                type Query {
+                  foo: String
+                }
+                """
+            )
+            assert_sdl_errors(
+                """
+                extend type Query @test(unknown: "")
+                """,
+                [
+                    {
+                        "message": "Unknown argument 'unknown' on directive '@test'.",
+                        "locations": [(2, 41)],
+                    },
+                ],
+                schema,
+            )
diff --git a/tests/validation/test_known_directives.py b/tests/validation/test_known_directives.py
new file mode 100644
index 0000000..142a25e
--- /dev/null
+++ b/tests/validation/test_known_directives.py
@@ -0,0 +1,405 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation import KnownDirectivesRule
+
+from .harness import assert_validation_errors, assert_sdl_validation_errors
+
+assert_errors = partial(assert_validation_errors, KnownDirectivesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+assert_sdl_errors = partial(assert_sdl_validation_errors, KnownDirectivesRule)
+
+assert_sdl_valid = partial(assert_sdl_errors, errors=[])
+
+
+schema_with_sdl_directives = build_schema(
+    """
+    directive @onSchema on SCHEMA
+    directive @onScalar on SCALAR
+    directive @onObject on OBJECT
+    directive @onFieldDefinition on FIELD_DEFINITION
+    directive @onArgumentDefinition on ARGUMENT_DEFINITION
+    directive @onInterface on INTERFACE
+    directive @onUnion on UNION
+    directive @onEnum on ENUM
+    directive @onEnumValue on ENUM_VALUE
+    directive @onInputObject on INPUT_OBJECT
+    directive @onInputFieldDefinition on INPUT_FIELD_DEFINITION
+    """
+)
+
+
+def describe_known_directives():
+    def with_no_directives():
+        assert_valid(
+            """
+            query Foo {
+              name
+              ...Frag
+            }
+
+            fragment Frag on Dog {
+              name
+            }
+            """
+        )
+
+    def with_known_directives():
+        assert_valid(
+            """
+            {
+              dog @include(if: true) {
+                name
+              }
+              human @skip(if: false) {
+                name
+              }
+            }
+            """
+        )
+
+    def with_unknown_directive():
+        assert_errors(
+            """
+            {
+              dog @unknown(directive: "value") {
+                name
+              }
+            }
+            """,
+            [{"message": "Unknown directive '@unknown'.", "locations": [(3, 19)]}],
+        )
+
+    def with_many_unknown_directives():
+        assert_errors(
+            """
+            {
+              dog @unknown(directive: "value") {
+                name
+              }
+              human @unknown(directive: "value") {
+                name
+                pets @unknown(directive: "value") {
+                  name
+                }
+              }
+            }
+            """,
+            [
+                {"message": "Unknown directive '@unknown'.", "locations": [(3, 19)]},
+                {"message": "Unknown directive '@unknown'.", "locations": [(6, 21)]},
+                {"message": "Unknown directive '@unknown'.", "locations": [(8, 22)]},
+            ],
+        )
+
+    def with_well_placed_directives():
+        assert_valid(
+            """
+            query ($var: Boolean) @onQuery {
+              name @include(if: $var)
+              ...Frag @include(if: true)
+              skippedField @skip(if: true)
+              ...SkippedFrag @skip(if: true)
+
+              ... @skip(if: true) {
+                skippedField
+              }
+            }
+
+            mutation @onMutation {
+              someField
+            }
+
+            subscription @onSubscription {
+              someField
+            }
+
+            fragment Frag on SomeType @onFragmentDefinition {
+              someField
+            }
+            """
+        )
+
+    def with_well_placed_variable_definition_directive():
+        assert_valid(
+            """
+            query Foo($var: Boolean @onVariableDefinition) {
+              name
+            }
+            """
+        )
+
+    def with_misplaced_directives():
+        assert_errors(
+            """
+            query Foo($var: Boolean) @include(if: true) {
+              name @onQuery @include(if: $var)
+              ...Frag @onQuery
+            }
+
+            mutation Bar @onQuery {
+              someField
+            }
+            """,
+            [
+                {
+                    "message": "Directive '@include' may not be used on query.",
+                    "locations": [(2, 38)],
+                },
+                {
+                    "message": "Directive '@onQuery' may not be used on field.",
+                    "locations": [(3, 20)],
+                },
+                {
+                    "message": "Directive '@onQuery'"
+                    " may not be used on fragment spread.",
+                    "locations": [(4, 23)],
+                },
+                {
+                    "message": "Directive '@onQuery' may not be used on mutation.",
+                    "locations": [(7, 26)],
+                },
+            ],
+        )
+
+    def with_misplaced_variable_definition_directive():
+        assert_errors(
+            """
+            query Foo($var: Boolean @onField) {
+              name
+            }
+            """,
+            [
+                {
+                    "message": "Directive '@onField'"
+                    " may not be used on variable definition.",
+                    "locations": [(2, 37)],
+                },
+            ],
+        )
+
+    def describe_within_sdl():
+        def with_directive_defined_inside_sdl():
+            assert_sdl_valid(
+                """
+                type Query {
+                  foo: String @test
+                }
+
+                directive @test on FIELD_DEFINITION
+                """
+            )
+
+        def with_standard_directive():
+            assert_sdl_valid(
+                """
+                type Query {
+                  foo: String @deprecated
+                }
+                """
+            )
+
+        def with_overridden_standard_directive():
+            assert_sdl_valid(
+                """
+                schema @deprecated {
+                  query: Query
+                }
+                directive @deprecated on SCHEMA
+                """
+            )
+
+        def with_directive_defined_in_schema_extension():
+            schema = build_schema(
+                """
+                type Query {
+                  foo: String
+                }
+                """
+            )
+            assert_sdl_valid(
+                """
+                directive @test on OBJECT
+
+                extend type Query @test
+                """,
+                schema=schema,
+            )
+
+        def with_directive_used_in_schema_extension():
+            schema = build_schema(
+                """
+                directive @test on OBJECT
+
+                type Query {
+                  foo: String
+                }
+                """
+            )
+            assert_sdl_valid(
+                """
+                extend type Query @test
+                """,
+                schema=schema,
+            )
+
+        def with_unknown_directive_in_schema_extension():
+            schema = build_schema(
+                """
+                type Query {
+                  foo: String
+                }
+                """
+            )
+            assert_sdl_errors(
+                """
+                extend type Query @unknown
+                """,
+                [{"message": "Unknown directive '@unknown'.", "locations": [(2, 35)]}],
+                schema,
+            )
+
+        def with_well_placed_directives():
+            assert_sdl_valid(
+                """
+                type MyObj implements MyInterface @onObject {
+                  myField(myArg: Int @onArgumentDefinition): String @onFieldDefinition
+                }
+
+                extend type MyObj @onObject
+
+                scalar MyScalar @onScalar
+
+                extend scalar MyScalar @onScalar
+
+                interface MyInterface @onInterface {
+                  myField(myArg: Int @onArgumentDefinition): String @onFieldDefinition
+                }
+
+                extend interface MyInterface @onInterface
+
+                union MyUnion @onUnion = MyObj | Other
+
+                extend union MyUnion @onUnion
+
+                enum MyEnum @onEnum {
+                  MY_VALUE @onEnumValue
+                }
+
+                extend enum MyEnum @onEnum
+
+                input MyInput @onInputObject {
+                  myField: Int @onInputFieldDefinition
+                }
+
+                extend input MyInput @onInputObject
+
+                schema @onSchema {
+                  query: MyQuery
+                }
+
+                extend schema @onSchema
+                """,
+                schema=schema_with_sdl_directives,
+            )
+
+        def with_misplaced_directives():
+            assert_sdl_errors(
+                """
+                type MyObj implements MyInterface @onInterface {
+                  myField(myArg: Int @onInputFieldDefinition): String @onInputFieldDefinition
+                }
+
+                scalar MyScalar @onEnum
+
+                interface MyInterface @onObject {
+                  myField(myArg: Int @onInputFieldDefinition): String @onInputFieldDefinition
+                }
+
+                union MyUnion @onEnumValue = MyObj | Other
+
+                enum MyEnum @onScalar {
+                  MY_VALUE @onUnion
+                }
+
+                input MyInput @onEnum {
+                  myField: Int @onArgumentDefinition
+                }
+
+                schema @onObject {
+                  query: MyQuery
+                }
+
+                extend schema @onObject
+                """,  # noqa: E501
+                [
+                    {
+                        "message": "Directive '@onInterface'"
+                        " may not be used on object.",
+                        "locations": [(2, 51)],
+                    },
+                    {
+                        "message": "Directive '@onInputFieldDefinition'"
+                        " may not be used on argument definition.",
+                        "locations": [(3, 38)],
+                    },
+                    {
+                        "message": "Directive '@onInputFieldDefinition'"
+                        " may not be used on field definition.",
+                        "locations": [(3, 71)],
+                    },
+                    {
+                        "message": "Directive '@onEnum' may not be used on scalar.",
+                        "locations": [(6, 33)],
+                    },
+                    {
+                        "message": "Directive '@onObject'"
+                        " may not be used on interface.",
+                        "locations": [(8, 39)],
+                    },
+                    {
+                        "message": "Directive '@onInputFieldDefinition'"
+                        " may not be used on argument definition.",
+                        "locations": [(9, 38)],
+                    },
+                    {
+                        "message": "Directive '@onInputFieldDefinition'"
+                        " may not be used on field definition.",
+                        "locations": [(9, 71)],
+                    },
+                    {
+                        "message": "Directive '@onEnumValue' may not be used on union.",
+                        "locations": [(12, 31)],
+                    },
+                    {
+                        "message": "Directive '@onScalar' may not be used on enum.",
+                        "locations": [(14, 29)],
+                    },
+                    {
+                        "message": "Directive '@onUnion'"
+                        " may not be used on enum value.",
+                        "locations": [(15, 28)],
+                    },
+                    {
+                        "message": "Directive '@onEnum'"
+                        " may not be used on input object.",
+                        "locations": [(18, 31)],
+                    },
+                    {
+                        "message": "Directive '@onArgumentDefinition'"
+                        " may not be used on input field definition.",
+                        "locations": [(19, 32)],
+                    },
+                    {
+                        "message": "Directive '@onObject' may not be used on schema.",
+                        "locations": [(22, 24)],
+                    },
+                    {
+                        "message": "Directive '@onObject' may not be used on schema.",
+                        "locations": [(26, 31)],
+                    },
+                ],
+                schema_with_sdl_directives,
+            )
diff --git a/tests/validation/test_known_fragment_names.py b/tests/validation/test_known_fragment_names.py
new file mode 100644
index 0000000..8a9b864
--- /dev/null
+++ b/tests/validation/test_known_fragment_names.py
@@ -0,0 +1,70 @@
+from functools import partial
+
+from graphql.validation import KnownFragmentNamesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, KnownFragmentNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_known_fragment_names():
+    def known_fragment_names_are_valid():
+        assert_valid(
+            """
+            {
+              human(id: 4) {
+                ...HumanFields1
+                  ... on Human {
+                    ...HumanFields2
+                  }
+                  ... {
+                    name
+                  }
+              }
+            }
+            fragment HumanFields1 on Human {
+              name
+              ...HumanFields3
+            }
+            fragment HumanFields2 on Human {
+              name
+            }
+            fragment HumanFields3 on Human {
+              name
+            }
+            """
+        )
+
+    def unknown_fragment_names_are_invalid():
+        assert_errors(
+            """
+            {
+              human(id: 4) {
+                ...UnknownFragment1
+                ... on Human {
+                  ...UnknownFragment2
+                }
+              }
+            }
+            fragment HumanFields on Human {
+              name
+              ...UnknownFragment3
+            }
+            """,
+            [
+                {
+                    "message": "Unknown fragment 'UnknownFragment1'.",
+                    "locations": [(4, 20)],
+                },
+                {
+                    "message": "Unknown fragment 'UnknownFragment2'.",
+                    "locations": [(6, 22)],
+                },
+                {
+                    "message": "Unknown fragment 'UnknownFragment3'.",
+                    "locations": [(12, 18)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_known_type_names.py b/tests/validation/test_known_type_names.py
new file mode 100644
index 0000000..7e9564e
--- /dev/null
+++ b/tests/validation/test_known_type_names.py
@@ -0,0 +1,317 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation import KnownTypeNamesRule
+
+from .harness import assert_validation_errors, assert_sdl_validation_errors
+
+assert_errors = partial(assert_validation_errors, KnownTypeNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+assert_sdl_errors = partial(assert_sdl_validation_errors, KnownTypeNamesRule)
+
+assert_sdl_valid = partial(assert_sdl_errors, errors=[])
+
+
+def describe_validate_known_type_names():
+    def known_type_names_are_valid():
+        assert_valid(
+            """
+            query Foo($var: String, $required: [String!]!) {
+              user(id: 4) {
+                pets { ... on Pet { name }, ...PetFields, ... { name } }
+              }
+            }
+            fragment PetFields on Pet {
+              name
+            }
+            """
+        )
+
+    def unknown_type_names_are_invalid():
+        assert_errors(
+            """
+            query Foo($var: JumbledUpLetters) {
+              user(id: 4) {
+                name
+                pets { ... on Badger { name }, ...PetFields, ... { name } }
+              }
+            }
+            fragment PetFields on Peat {
+              name
+            }
+            """,
+            [
+                {
+                    "message": "Unknown type 'JumbledUpLetters'.",
+                    "locations": [(2, 29)],
+                },
+                {"message": "Unknown type 'Badger'.", "locations": [(5, 31)]},
+                {
+                    "message": "Unknown type 'Peat'. Did you mean 'Pet' or 'Cat'?",
+                    "locations": [(8, 35)],
+                },
+            ],
+        )
+
+    def references_to_standard_scalars_that_are_missing_in_schema():
+        schema = build_schema("type Query { foo: String }")
+        query = """
+            query ($id: ID, $float: Float, $int: Int) {
+              __typename
+            }
+            """
+        assert_errors(
+            query,
+            [
+                {"message": "Unknown type 'ID'.", "locations": [(2, 25)]},
+                {"message": "Unknown type 'Float'.", "locations": [(2, 37)]},
+                {"message": "Unknown type 'Int'.", "locations": [(2, 50)]},
+            ],
+            schema,
+        )
+
+    def reference_types_defined_inside_the_same_document():
+        assert_sdl_valid(
+            """
+            union SomeUnion = SomeObject | AnotherObject
+
+            type SomeObject implements SomeInterface {
+            someScalar(arg: SomeInputObject): SomeScalar
+            }
+
+            type AnotherObject {
+            foo(arg: SomeInputObject): String
+            }
+
+            type SomeInterface {
+            someScalar(arg: SomeInputObject): SomeScalar
+            }
+
+            input SomeInputObject {
+            someScalar: SomeScalar
+            }
+
+            scalar SomeScalar
+
+            type RootQuery {
+            someInterface: SomeInterface
+            someUnion: SomeUnion
+            someScalar: SomeScalar
+            someObject: SomeObject
+            }
+
+            schema {
+            query: RootQuery
+            }
+            """
+        )
+
+    def unknown_type_references():
+        assert_sdl_errors(
+            """
+            type A
+            type B
+
+            type SomeObject implements C {
+              e(d: D): E
+            }
+
+            union SomeUnion = F | G
+
+            interface SomeInterface {
+              i(h: H): I
+            }
+
+            input SomeInput {
+              j: J
+            }
+
+            directive @SomeDirective(k: K) on QUERY
+
+            schema {
+              query: L
+              mutation: M
+              subscription: N
+            }
+            """,
+            [
+                {
+                    "message": "Unknown type 'C'. Did you mean 'A' or 'B'?",
+                    "locations": [(5, 40)],
+                },
+                {
+                    "message": "Unknown type 'D'. Did you mean 'A', 'B', or 'ID'?",
+                    "locations": [(6, 20)],
+                },
+                {
+                    "message": "Unknown type 'E'. Did you mean 'A' or 'B'?",
+                    "locations": [(6, 24)],
+                },
+                {
+                    "message": "Unknown type 'F'. Did you mean 'A' or 'B'?",
+                    "locations": [(9, 31)],
+                },
+                {
+                    "message": "Unknown type 'G'. Did you mean 'A' or 'B'?",
+                    "locations": [(9, 35)],
+                },
+                {
+                    "message": "Unknown type 'H'. Did you mean 'A' or 'B'?",
+                    "locations": [(12, 20)],
+                },
+                {
+                    "message": "Unknown type 'I'. Did you mean 'A', 'B', or 'ID'?",
+                    "locations": [(12, 24)],
+                },
+                {
+                    "message": "Unknown type 'J'. Did you mean 'A' or 'B'?",
+                    "locations": [(16, 18)],
+                },
+                {
+                    "message": "Unknown type 'K'. Did you mean 'A' or 'B'?",
+                    "locations": [(19, 41)],
+                },
+                {
+                    "message": "Unknown type 'L'. Did you mean 'A' or 'B'?",
+                    "locations": [(22, 22)],
+                },
+                {
+                    "message": "Unknown type 'M'. Did you mean 'A' or 'B'?",
+                    "locations": [(23, 25)],
+                },
+                {
+                    "message": "Unknown type 'N'. Did you mean 'A' or 'B'?",
+                    "locations": [(24, 29)],
+                },
+            ],
+        )
+
+    def does_not_consider_non_type_definitions():
+        assert_sdl_errors(
+            """
+            query Foo { __typename }
+            fragment Foo on Query { __typename }
+            directive @Foo on QUERY
+
+            type Query {
+              foo: Foo
+            }
+            """,
+            [{"message": "Unknown type 'Foo'.", "locations": [(7, 20)]}],
+        )
+
+    def reference_standard_scalars_inside_extension_document():
+        schema = build_schema("type Foo")
+        sdl = """
+            type SomeType {
+              string: String
+              int: Int
+              float: Float
+              boolean: Boolean
+              id: ID
+            }
+            """
+
+        assert_sdl_valid(sdl, schema=schema)
+
+    def reference_types_inside_extension_document():
+        schema = build_schema("type Foo")
+        sdl = """
+            type QueryRoot {
+              foo: Foo
+              bar: Bar
+            }
+
+            scalar Bar
+
+            schema {
+              query: QueryRoot
+            }
+            """
+
+        assert_sdl_valid(sdl, schema=schema)
+
+    def unknown_type_references_inside_extension_document():
+        schema = build_schema("type A")
+        sdl = """
+            type B
+
+            type SomeObject implements C {
+              e(d: D): E
+            }
+
+            union SomeUnion = F | G
+
+            interface SomeInterface {
+              i(h: H): I
+            }
+
+            input SomeInput {
+              j: J
+            }
+
+            directive @SomeDirective(k: K) on QUERY
+
+            schema {
+              query: L
+              mutation: M
+              subscription: N
+            }
+            """
+
+        assert_sdl_errors(
+            sdl,
+            [
+                {
+                    "message": "Unknown type 'C'. Did you mean 'A' or 'B'?",
+                    "locations": [(4, 40)],
+                },
+                {
+                    "message": "Unknown type 'D'. Did you mean 'A', 'B', or 'ID'?",
+                    "locations": [(5, 20)],
+                },
+                {
+                    "message": "Unknown type 'E'. Did you mean 'A' or 'B'?",
+                    "locations": [(5, 24)],
+                },
+                {
+                    "message": "Unknown type 'F'. Did you mean 'A' or 'B'?",
+                    "locations": [(8, 31)],
+                },
+                {
+                    "message": "Unknown type 'G'. Did you mean 'A' or 'B'?",
+                    "locations": [(8, 35)],
+                },
+                {
+                    "message": "Unknown type 'H'. Did you mean 'A' or 'B'?",
+                    "locations": [(11, 20)],
+                },
+                {
+                    "message": "Unknown type 'I'. Did you mean 'A', 'B', or 'ID'?",
+                    "locations": [(11, 24)],
+                },
+                {
+                    "message": "Unknown type 'J'. Did you mean 'A' or 'B'?",
+                    "locations": [(15, 18)],
+                },
+                {
+                    "message": "Unknown type 'K'. Did you mean 'A' or 'B'?",
+                    "locations": [(18, 41)],
+                },
+                {
+                    "message": "Unknown type 'L'. Did you mean 'A' or 'B'?",
+                    "locations": [(21, 22)],
+                },
+                {
+                    "message": "Unknown type 'M'. Did you mean 'A' or 'B'?",
+                    "locations": [(22, 25)],
+                },
+                {
+                    "message": "Unknown type 'N'. Did you mean 'A' or 'B'?",
+                    "locations": [(23, 29)],
+                },
+            ],
+            schema,
+        )
diff --git a/tests/validation/test_lone_anonymous_operation.py b/tests/validation/test_lone_anonymous_operation.py
new file mode 100644
index 0000000..83e431b
--- /dev/null
+++ b/tests/validation/test_lone_anonymous_operation.py
@@ -0,0 +1,116 @@
+from functools import partial
+
+from graphql.validation import LoneAnonymousOperationRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, LoneAnonymousOperationRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_anonymous_operation_must_be_alone():
+    def no_operations():
+        assert_valid(
+            """
+            fragment fragA on Type {
+              field
+            }
+            """
+        )
+
+    def one_anon_operation():
+        assert_valid(
+            """
+            {
+              field
+            }
+            """
+        )
+
+    def multiple_named_operation():
+        assert_valid(
+            """
+            query Foo {
+              field
+            }
+
+            query Bar {
+              field
+            }
+            """
+        )
+
+    def anon_operation_with_fragment():
+        assert_valid(
+            """
+            {
+              ...Foo
+            }
+            fragment Foo on Type {
+              field
+            }
+            """
+        )
+
+    def multiple_anon_operations():
+        assert_errors(
+            """
+            {
+              fieldA
+            }
+            {
+              fieldB
+            }
+            """,
+            [
+                {
+                    "message": "This anonymous operation"
+                    " must be the only defined operation.",
+                    "locations": [(2, 13)],
+                },
+                {
+                    "message": "This anonymous operation"
+                    " must be the only defined operation.",
+                    "locations": [(5, 13)],
+                },
+            ],
+        )
+
+    def anon_operation_with_a_mutation():
+        assert_errors(
+            """
+            {
+              fieldA
+            }
+            mutation Foo {
+              fieldB
+            }
+            """,
+            [
+                {
+                    "message": "This anonymous operation"
+                    " must be the only defined operation.",
+                    "locations": [(2, 13)],
+                },
+            ],
+        )
+
+    def anon_operation_with_a_subscription():
+        assert_errors(
+            """
+            {
+              fieldA
+            }
+            subscription Foo {
+              fieldB
+            }
+            """,
+            [
+                {
+                    "message": "This anonymous operation"
+                    " must be the only defined operation.",
+                    "locations": [(2, 13)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_lone_schema_definition.py b/tests/validation/test_lone_schema_definition.py
new file mode 100644
index 0000000..9b6d88f
--- /dev/null
+++ b/tests/validation/test_lone_schema_definition.py
@@ -0,0 +1,161 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation.rules.lone_schema_definition import LoneSchemaDefinitionRule
+
+from .harness import assert_sdl_validation_errors
+
+assert_sdl_errors = partial(assert_sdl_validation_errors, LoneSchemaDefinitionRule)
+
+assert_sdl_valid = partial(assert_sdl_errors, errors=[])
+
+
+def describe_validate_schema_definition_should_be_alone():
+    def no_schema():
+        assert_sdl_valid(
+            """
+            type Query {
+              foo: String
+            }
+            """
+        )
+
+    def one_schema_definition():
+        assert_sdl_valid(
+            """
+            schema {
+              query: Foo
+            }
+
+            type Foo {
+              foo: String
+            }
+            """
+        )
+
+    def multiple_schema_definitions():
+        assert_sdl_errors(
+            """
+            schema {
+              query: Foo
+            }
+
+            type Foo {
+              foo: String
+            }
+
+            schema {
+              mutation: Foo
+            }
+
+            schema {
+              subscription: Foo
+            }
+            """,
+            [
+                {
+                    "message": "Must provide only one schema definition.",
+                    "locations": [(10, 13)],
+                },
+                {
+                    "message": "Must provide only one schema definition.",
+                    "locations": [(14, 13)],
+                },
+            ],
+        )
+
+    def define_schema_in_schema_extension():
+        schema = build_schema(
+            """
+            type Foo {
+              foo: String
+            }
+            """
+        )
+
+        assert_sdl_valid(
+            """
+            schema {
+              query: Foo
+            }
+            """,
+            schema=schema,
+        )
+
+    def redefine_schema_in_schema_extension():
+        schema = build_schema(
+            """
+            schema {
+              query: Foo
+            }
+
+            type Foo {
+              foo: String
+            }
+            """
+        )
+
+        assert_sdl_errors(
+            """
+            schema {
+              mutation: Foo
+            }
+            """,
+            [
+                {
+                    "message": "Cannot define a new schema within a schema extension.",
+                    "locations": [(2, 13)],
+                }
+            ],
+            schema,
+        )
+
+    def redefine_implicit_schema_in_schema_extension():
+        schema = build_schema(
+            """
+            type Query {
+              fooField: Foo
+            }
+
+            type Foo {
+              foo: String
+            }
+            """
+        )
+
+        assert_sdl_errors(
+            """
+            schema {
+              mutation: Foo
+            }
+            """,
+            [
+                {
+                    "message": "Cannot define a new schema within a schema extension.",
+                    "locations": [(2, 13)],
+                },
+            ],
+            schema,
+        )
+
+    def extend_schema_in_schema_extension():
+        schema = build_schema(
+            """
+            type Query {
+              fooField: Foo
+            }
+
+            type Foo {
+              foo: String
+            }
+            """
+        )
+
+        assert_sdl_valid(
+            """
+            extend schema {
+              mutation: Foo
+            }
+            """,
+            schema=schema,
+        )
diff --git a/tests/validation/test_no_fragment_cycles.py b/tests/validation/test_no_fragment_cycles.py
new file mode 100644
index 0000000..3bc60a0
--- /dev/null
+++ b/tests/validation/test_no_fragment_cycles.py
@@ -0,0 +1,250 @@
+from functools import partial
+
+from graphql.validation import NoFragmentCyclesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, NoFragmentCyclesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_no_circular_fragment_spreads():
+    def single_reference_is_valid():
+        assert_valid(
+            """
+            fragment fragA on Dog { ...fragB }
+            fragment fragB on Dog { name }
+            """
+        )
+
+    def spreading_twice_is_not_circular():
+        assert_valid(
+            """
+            fragment fragA on Dog { ...fragB, ...fragB }
+            fragment fragB on Dog { name }
+            """
+        )
+
+    def spreading_twice_indirectly_is_not_circular():
+        assert_valid(
+            """
+            fragment fragA on Dog { ...fragB, ...fragC }
+            fragment fragB on Dog { ...fragC }
+            fragment fragC on Dog { name }
+            """
+        )
+
+    def double_spread_within_abstract_types():
+        assert_valid(
+            """
+            fragment nameFragment on Pet {
+              ... on Dog { name }
+              ... on Cat { name }
+            }
+            fragment spreadsInAnon on Pet {
+              ... on Dog { ...nameFragment }
+              ... on Cat { ...nameFragment }
+            }
+            """
+        )
+
+    def does_not_raise_false_positive_on_unknown_fragment():
+        assert_valid(
+            """
+            fragment nameFragment on Pet {
+              ...UnknownFragment
+            }
+            """
+        )
+
+    def spreading_recursively_within_field_fails():
+        assert_errors(
+            """
+            fragment fragA on Human { relatives { ...fragA } },
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragA' within itself.",
+                    "locations": [(2, 51)],
+                }
+            ],
+        )
+
+    def no_spreading_itself_directly():
+        assert_errors(
+            """
+            fragment fragA on Dog { ...fragA }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragA' within itself.",
+                    "locations": [(2, 37)],
+                }
+            ],
+        )
+
+    def no_spreading_itself_directly_within_inline_fragment():
+        assert_errors(
+            """
+            fragment fragA on Pet {
+              ... on Dog {
+                ...fragA
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragA' within itself.",
+                    "locations": [(4, 17)],
+                }
+            ],
+        )
+
+    def no_spreading_itself_indirectly():
+        assert_errors(
+            """
+            fragment fragA on Dog { ...fragB }
+            fragment fragB on Dog { ...fragA }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragA'"
+                    " within itself via 'fragB'.",
+                    "locations": [(2, 37), (3, 37)],
+                }
+            ],
+        )
+
+    def no_spreading_itself_indirectly_reports_opposite_order():
+        assert_errors(
+            """
+            fragment fragB on Dog { ...fragA }
+            fragment fragA on Dog { ...fragB }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragB'"
+                    " within itself via 'fragA'.",
+                    "locations": [(2, 37), (3, 37)],
+                }
+            ],
+        )
+
+    def no_spreading_itself_indirectly_within_inline_fragment():
+        assert_errors(
+            """
+            fragment fragA on Pet {
+              ... on Dog {
+                ...fragB
+              }
+            }
+            fragment fragB on Pet {
+              ... on Dog {
+                ...fragA
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragA'"
+                    " within itself via 'fragB'.",
+                    "locations": [(4, 17), (9, 17)],
+                }
+            ],
+        )
+
+    def no_spreading_itself_deeply():
+        assert_errors(
+            """
+            fragment fragA on Dog { ...fragB }
+            fragment fragB on Dog { ...fragC }
+            fragment fragC on Dog { ...fragO }
+            fragment fragX on Dog { ...fragY }
+            fragment fragY on Dog { ...fragZ }
+            fragment fragZ on Dog { ...fragO }
+            fragment fragO on Dog { ...fragP }
+            fragment fragP on Dog { ...fragA, ...fragX }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragA' within itself"
+                    " via 'fragB', 'fragC', 'fragO', 'fragP'.",
+                    "locations": [(2, 37), (3, 37), (4, 37), (8, 37), (9, 37)],
+                    "path": None,
+                },
+                {
+                    "message": "Cannot spread fragment 'fragO' within itself"
+                    " via 'fragP', 'fragX', 'fragY', 'fragZ'.",
+                    "locations": [(8, 37), (9, 47), (5, 37), (6, 37), (7, 37)],
+                    "path": None,
+                },
+            ],
+        )
+
+    def no_spreading_itself_deeply_two_paths():
+        assert_errors(
+            """
+            fragment fragA on Dog { ...fragB, ...fragC }
+            fragment fragB on Dog { ...fragA }
+            fragment fragC on Dog { ...fragA }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragA'"
+                    " within itself via 'fragB'.",
+                    "locations": [(2, 37), (3, 37)],
+                },
+                {
+                    "message": "Cannot spread fragment 'fragA'"
+                    " within itself via 'fragC'.",
+                    "locations": [(2, 47), (4, 37)],
+                },
+            ],
+        )
+
+    def no_spreading_itself_deeply_two_paths_alt_traverse_order():
+        assert_errors(
+            """
+            fragment fragA on Dog { ...fragC }
+            fragment fragB on Dog { ...fragC }
+            fragment fragC on Dog { ...fragA, ...fragB }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragA'"
+                    " within itself via 'fragC'.",
+                    "locations": [(2, 37), (4, 37)],
+                },
+                {
+                    "message": "Cannot spread fragment 'fragC'"
+                    " within itself via 'fragB'.",
+                    "locations": [(4, 47), (3, 37)],
+                },
+            ],
+        )
+
+    def no_spreading_itself_deeply_and_immediately():
+        assert_errors(
+            """
+            fragment fragA on Dog { ...fragB }
+            fragment fragB on Dog { ...fragB, ...fragC }
+            fragment fragC on Dog { ...fragA, ...fragB }
+            """,
+            [
+                {
+                    "message": "Cannot spread fragment 'fragB' within itself.",
+                    "locations": [(3, 37)],
+                },
+                {
+                    "message": "Cannot spread fragment 'fragA'"
+                    " within itself via 'fragB', 'fragC'.",
+                    "locations": [(2, 37), (3, 47), (4, 37)],
+                },
+                {
+                    "message": "Cannot spread fragment 'fragB'"
+                    " within itself via 'fragC'.",
+                    "locations": [(3, 47), (4, 47)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_no_undefined_variables.py b/tests/validation/test_no_undefined_variables.py
new file mode 100644
index 0000000..f953723
--- /dev/null
+++ b/tests/validation/test_no_undefined_variables.py
@@ -0,0 +1,370 @@
+from functools import partial
+
+from graphql.validation import NoUndefinedVariablesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, NoUndefinedVariablesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_no_undefined_variables():
+    def all_variables_defined():
+        assert_valid(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              field(a: $a, b: $b, c: $c)
+            }
+            """
+        )
+
+    def all_variables_deeply_defined():
+        assert_valid(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              field(a: $a) {
+                field(b: $b) {
+                  field(c: $c)
+                }
+              }
+            }
+            """
+        )
+
+    def all_variables_deeply_in_inline_fragments_defined():
+        assert_valid(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              ... on Type {
+                field(a: $a) {
+                  field(b: $b) {
+                    ... on Type {
+                      field(c: $c)
+                    }
+                  }
+                }
+              }
+            }
+            """
+        )
+
+    def all_variables_in_fragments_deeply_defined():
+        assert_valid(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a) {
+                ...FragB
+              }
+            }
+            fragment FragB on Type {
+              field(b: $b) {
+                ...FragC
+              }
+            }
+            fragment FragC on Type {
+              field(c: $c)
+            }
+            """
+        )
+
+    def variable_within_single_fragment_defined_in_multiple_operations():
+        assert_valid(
+            """
+            query Foo($a: String) {
+              ...FragA
+            }
+            query Bar($a: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a)
+            }
+            """
+        )
+
+    def variable_within_fragments_defined_in_operations():
+        assert_valid(
+            """
+            query Foo($a: String) {
+              ...FragA
+            }
+            query Bar($b: String) {
+              ...FragB
+            }
+            fragment FragA on Type {
+              field(a: $a)
+            }
+            fragment FragB on Type {
+              field(b: $b)
+            }
+            """
+        )
+
+    def variable_within_recursive_fragment_defined():
+        assert_valid(
+            """
+            query Foo($a: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a) {
+                ...FragA
+              }
+            }
+            """
+        )
+
+    def variable_not_defined():
+        assert_errors(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              field(a: $a, b: $b, c: $c, d: $d)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$d' is not defined by operation 'Foo'.",
+                    "locations": [(3, 45), (2, 13)],
+                },
+            ],
+        )
+
+    def variable_not_defined_by_unnamed_query():
+        assert_errors(
+            """
+            {
+              field(a: $a)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is not defined.",
+                    "locations": [(3, 24), (2, 13)],
+                },
+            ],
+        )
+
+    def multiple_variables_not_defined():
+        assert_errors(
+            """
+            query Foo($b: String) {
+              field(a: $a, b: $b, c: $c)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is not defined by operation 'Foo'.",
+                    "locations": [(3, 24), (2, 13)],
+                },
+                {
+                    "message": "Variable '$c' is not defined by operation 'Foo'.",
+                    "locations": [(3, 38), (2, 13)],
+                },
+            ],
+        )
+
+    def variable_in_fragment_not_defined_by_unnamed_query():
+        assert_errors(
+            """
+            {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is not defined.",
+                    "locations": [(6, 24), (2, 13)],
+                },
+            ],
+        )
+
+    def variable_in_fragment_not_defined_by_operation():
+        assert_errors(
+            """
+            query Foo($a: String, $b: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a) {
+                ...FragB
+              }
+            }
+            fragment FragB on Type {
+              field(b: $b) {
+                ...FragC
+              }
+            }
+            fragment FragC on Type {
+              field(c: $c)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$c' is not defined by operation 'Foo'.",
+                    "locations": [(16, 24), (2, 13)],
+                },
+            ],
+        )
+
+    def multiple_variables_in_fragments_not_defined():
+        assert_errors(
+            """
+            query Foo($b: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a) {
+                ...FragB
+              }
+            }
+            fragment FragB on Type {
+              field(b: $b) {
+                ...FragC
+              }
+            }
+            fragment FragC on Type {
+              field(c: $c)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is not defined by operation 'Foo'.",
+                    "locations": [(6, 24), (2, 13)],
+                },
+                {
+                    "message": "Variable '$c' is not defined by operation 'Foo'.",
+                    "locations": [(16, 24), (2, 13)],
+                },
+            ],
+        )
+
+    def single_variable_in_fragment_not_defined_by_multiple_operations():
+        assert_errors(
+            """
+            query Foo($a: String) {
+              ...FragAB
+            }
+            query Bar($a: String) {
+              ...FragAB
+            }
+            fragment FragAB on Type {
+              field(a: $a, b: $b)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$b' is not defined by operation 'Foo'.",
+                    "locations": [(9, 31), (2, 13)],
+                },
+                {
+                    "message": "Variable '$b' is not defined by operation 'Bar'.",
+                    "locations": [(9, 31), (5, 13)],
+                },
+            ],
+        )
+
+    def variables_in_fragment_not_defined_by_multiple_operations():
+        assert_errors(
+            """
+            query Foo($b: String) {
+              ...FragAB
+            }
+            query Bar($a: String) {
+              ...FragAB
+            }
+            fragment FragAB on Type {
+              field(a: $a, b: $b)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is not defined by operation 'Foo'.",
+                    "locations": [(9, 24), (2, 13)],
+                },
+                {
+                    "message": "Variable '$b' is not defined by operation 'Bar'.",
+                    "locations": [(9, 31), (5, 13)],
+                },
+            ],
+        )
+
+    def variable_in_fragment_used_by_other_operation():
+        assert_errors(
+            """
+            query Foo($b: String) {
+              ...FragA
+            }
+            query Bar($a: String) {
+              ...FragB
+            }
+            fragment FragA on Type {
+              field(a: $a)
+            }
+            fragment FragB on Type {
+              field(b: $b)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is not defined by operation 'Foo'.",
+                    "locations": [(9, 24), (2, 13)],
+                },
+                {
+                    "message": "Variable '$b' is not defined by operation 'Bar'.",
+                    "locations": [(12, 24), (5, 13)],
+                },
+            ],
+        )
+
+    def multiple_undefined_variables_produce_multiple_errors():
+        assert_errors(
+            """
+            query Foo($b: String) {
+              ...FragAB
+            }
+            query Bar($a: String) {
+              ...FragAB
+            }
+            fragment FragAB on Type {
+              field1(a: $a, b: $b)
+              ...FragC
+              field3(a: $a, b: $b)
+            }
+            fragment FragC on Type {
+              field2(c: $c)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is not defined by operation 'Foo'.",
+                    "locations": [(9, 25), (2, 13)],
+                },
+                {
+                    "message": "Variable '$a' is not defined by operation 'Foo'.",
+                    "locations": [(11, 25), (2, 13)],
+                },
+                {
+                    "message": "Variable '$c' is not defined by operation 'Foo'.",
+                    "locations": [(14, 25), (2, 13)],
+                },
+                {
+                    "message": "Variable '$b' is not defined by operation 'Bar'.",
+                    "locations": [(9, 32), (5, 13)],
+                },
+                {
+                    "message": "Variable '$b' is not defined by operation 'Bar'.",
+                    "locations": [(11, 32), (5, 13)],
+                },
+                {
+                    "message": "Variable '$c' is not defined by operation 'Bar'.",
+                    "locations": [(14, 25), (5, 13)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_no_unused_fragments.py b/tests/validation/test_no_unused_fragments.py
new file mode 100644
index 0000000..f317d1f
--- /dev/null
+++ b/tests/validation/test_no_unused_fragments.py
@@ -0,0 +1,162 @@
+from functools import partial
+
+from graphql.validation import NoUnusedFragmentsRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, NoUnusedFragmentsRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_no_unused_fragments():
+    def all_fragment_names_are_used():
+        assert_valid(
+            """
+            {
+              human(id: 4) {
+                ...HumanFields1
+                ... on Human {
+                  ...HumanFields2
+                }
+              }
+            }
+            fragment HumanFields1 on Human {
+              name
+              ...HumanFields3
+            }
+            fragment HumanFields2 on Human {
+              name
+            }
+            fragment HumanFields3 on Human {
+              name
+            }
+            """
+        )
+
+    def all_fragment_names_are_used_by_multiple_operations():
+        assert_valid(
+            """
+            query Foo {
+              human(id: 4) {
+                ...HumanFields1
+              }
+            }
+            query Bar {
+              human(id: 4) {
+                ...HumanFields2
+              }
+            }
+            fragment HumanFields1 on Human {
+              name
+              ...HumanFields3
+            }
+            fragment HumanFields2 on Human {
+              name
+            }
+            fragment HumanFields3 on Human {
+              name
+            }
+            """
+        )
+
+    def contains_unknown_fragments():
+        assert_errors(
+            """
+            query Foo {
+              human(id: 4) {
+                ...HumanFields1
+              }
+            }
+            query Bar {
+              human(id: 4) {
+                ...HumanFields2
+              }
+            }
+            fragment HumanFields1 on Human {
+              name
+              ...HumanFields3
+            }
+            fragment HumanFields2 on Human {
+              name
+            }
+            fragment HumanFields3 on Human {
+              name
+            }
+            fragment Unused1 on Human {
+              name
+            }
+            fragment Unused2 on Human {
+              name
+            }
+            """,
+            [
+                {
+                    "message": "Fragment 'Unused1' is never used.",
+                    "locations": [(22, 13)],
+                },
+                {
+                    "message": "Fragment 'Unused2' is never used.",
+                    "locations": [(25, 13)],
+                },
+            ],
+        )
+
+    def contains_unknown_fragments_with_ref_cycle():
+        assert_errors(
+            """
+            query Foo {
+              human(id: 4) {
+                ...HumanFields1
+              }
+            }
+            query Bar {
+              human(id: 4) {
+                ...HumanFields2
+              }
+            }
+            fragment HumanFields1 on Human {
+              name
+              ...HumanFields3
+            }
+            fragment HumanFields2 on Human {
+              name
+            }
+            fragment HumanFields3 on Human {
+              name
+            }
+            fragment Unused1 on Human {
+              name
+              ...Unused2
+            }
+            fragment Unused2 on Human {
+              name
+              ...Unused1
+            }
+            """,
+            [
+                {
+                    "message": "Fragment 'Unused1' is never used.",
+                    "locations": [(22, 13)],
+                },
+                {
+                    "message": "Fragment 'Unused2' is never used.",
+                    "locations": [(26, 13)],
+                },
+            ],
+        )
+
+    def contains_unknown_and_undefined_fragments():
+        assert_errors(
+            """
+            query Foo {
+              human(id: 4) {
+                ...bar
+              }
+            }
+            fragment foo on Human {
+              name
+            }
+            """,
+            [{"message": "Fragment 'foo' is never used.", "locations": [(7, 13)]}],
+        )
diff --git a/tests/validation/test_no_unused_variables.py b/tests/validation/test_no_unused_variables.py
new file mode 100644
index 0000000..7366ba5
--- /dev/null
+++ b/tests/validation/test_no_unused_variables.py
@@ -0,0 +1,242 @@
+from functools import partial
+
+from graphql.validation import NoUnusedVariablesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, NoUnusedVariablesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_no_unused_variables():
+    def uses_all_variables():
+        assert_valid(
+            """
+            query ($a: String, $b: String, $c: String) {
+              field(a: $a, b: $b, c: $c)
+            }
+            """
+        )
+
+    def uses_all_variables_deeply():
+        assert_valid(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              field(a: $a) {
+                field(b: $b) {
+                  field(c: $c)
+                }
+              }
+            }
+            """
+        )
+
+    def uses_all_variables_deeply_in_inline_fragments():
+        assert_valid(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              ... on Type {
+                field(a: $a) {
+                  field(b: $b) {
+                    ... on Type {
+                      field(c: $c)
+                    }
+                  }
+                }
+              }
+            }
+            """
+        )
+
+    def uses_all_variables_in_fragment():
+        assert_valid(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a) {
+                ...FragB
+              }
+            }
+            fragment FragB on Type {
+              field(b: $b) {
+                ...FragC
+              }
+            }
+            fragment FragC on Type {
+              field(c: $c)
+            }
+            """
+        )
+
+    def variable_used_by_fragment_in_multiple_operations():
+        assert_valid(
+            """
+            query Foo($a: String) {
+              ...FragA
+            }
+            query Bar($b: String) {
+              ...FragB
+            }
+            fragment FragA on Type {
+              field(a: $a)
+            }
+            fragment FragB on Type {
+              field(b: $b)
+            }
+            """
+        )
+
+    def variable_used_by_recursive_fragment():
+        assert_valid(
+            """
+            query Foo($a: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a) {
+                ...FragA
+              }
+            }
+            """
+        )
+
+    def variable_not_used():
+        assert_errors(
+            """
+            query ($a: String, $b: String, $c: String) {
+              field(a: $a, b: $b)
+            }
+            """,
+            [{"message": "Variable '$c' is never used.", "locations": [(2, 44)]}],
+        )
+
+    def multiple_variables_not_used():
+        assert_errors(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              field(b: $b)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is never used in operation 'Foo'.",
+                    "locations": [(2, 23)],
+                },
+                {
+                    "message": "Variable '$c' is never used in operation 'Foo'.",
+                    "locations": [(2, 47)],
+                },
+            ],
+        )
+
+    def variable_not_used_in_fragments():
+        assert_errors(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a) {
+                ...FragB
+              }
+            }
+            fragment FragB on Type {
+              field(b: $b) {
+                ...FragC
+              }
+            }
+            fragment FragC on Type {
+              field
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$c' is never used in operation 'Foo'.",
+                    "locations": [(2, 47)],
+                },
+            ],
+        )
+
+    def multiple_variables_not_used_in_fragments():
+        assert_errors(
+            """
+            query Foo($a: String, $b: String, $c: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field {
+                ...FragB
+              }
+            }
+            fragment FragB on Type {
+              field(b: $b) {
+                ...FragC
+              }
+            }
+            fragment FragC on Type {
+              field
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$a' is never used in operation 'Foo'.",
+                    "locations": [(2, 23)],
+                },
+                {
+                    "message": "Variable '$c' is never used in operation 'Foo'.",
+                    "locations": [(2, 47)],
+                },
+            ],
+        )
+
+    def variable_not_used_by_unreferenced_fragment():
+        assert_errors(
+            """
+            query Foo($b: String) {
+              ...FragA
+            }
+            fragment FragA on Type {
+              field(a: $a)
+            }
+            fragment FragB on Type {
+              field(b: $b)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$b' is never used in operation 'Foo'.",
+                    "locations": [(2, 23)],
+                },
+            ],
+        )
+
+    def variable_not_used_by_fragment_used_by_other_operation():
+        assert_errors(
+            """
+            query Foo($b: String) {
+              ...FragA
+            }
+            query Bar($a: String) {
+              ...FragB
+            }
+            fragment FragA on Type {
+              field(a: $a)
+            }
+            fragment FragB on Type {
+              field(b: $b)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$b' is never used in operation 'Foo'.",
+                    "locations": [(2, 23)],
+                },
+                {
+                    "message": "Variable '$a' is never used in operation 'Bar'.",
+                    "locations": [(5, 23)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_overlapping_fields_can_be_merged.py b/tests/validation/test_overlapping_fields_can_be_merged.py
new file mode 100644
index 0000000..145cf63
--- /dev/null
+++ b/tests/validation/test_overlapping_fields_can_be_merged.py
@@ -0,0 +1,1083 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation import OverlappingFieldsCanBeMergedRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, OverlappingFieldsCanBeMergedRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_overlapping_fields_can_be_merged():
+    def unique_fields():
+        assert_valid(
+            """
+            fragment uniqueFields on Dog {
+              name
+              nickname
+            }
+            """
+        )
+
+    def identical_fields():
+        assert_valid(
+            """
+            fragment mergeIdenticalFields on Dog {
+              name
+              name
+            }
+            """
+        )
+
+    def identical_fields_with_identical_args():
+        assert_valid(
+            """
+            fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
+              doesKnowCommand(dogCommand: SIT)
+              doesKnowCommand(dogCommand: SIT)
+            }
+            """
+        )
+
+    def identical_fields_with_identical_directives():
+        assert_valid(
+            """
+            fragment mergeSameFieldsWithSameDirectives on Dog {
+              name @include(if: true)
+              name @include(if: true)
+            }
+            """
+        )
+
+    def different_args_with_different_aliases():
+        assert_valid(
+            """
+            fragment differentArgsWithDifferentAliases on Dog {
+              knowsSit: doesKnowCommand(dogCommand: SIT)
+              knowsDown: doesKnowCommand(dogCommand: DOWN)
+            }
+            """
+        )
+
+    def different_directives_with_different_aliases():
+        assert_valid(
+            """
+            fragment differentDirectivesWithDifferentAliases on Dog {
+              nameIfTrue: name @include(if: true)
+              nameIfFalse: name @include(if: false)
+            }
+            """
+        )
+
+    def different_skip_or_include_directives_accepted():
+        # Note: Differing skip/include directives don't create an ambiguous
+        # return value and are acceptable in conditions where differing runtime
+        # values may have the same desired effect of including/skipping a field
+        assert_valid(
+            """
+            fragment differentDirectivesWithDifferentAliases on Dog {
+              name @include(if: true)
+              name @include(if: false)
+            }
+            """
+        )
+
+    def same_aliases_with_different_field_targets():
+        assert_errors(
+            """
+            fragment sameAliasesWithDifferentFieldTargets on Dog {
+              fido: name
+              fido: nickname
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'fido' conflict"
+                    " because 'name' and 'nickname' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(3, 15), (4, 15)],
+                    "path": None,
+                }
+            ],
+        )
+
+    def same_aliases_allowed_on_non_overlapping_fields():
+        assert_valid(
+            """
+            fragment sameAliasesWithDifferentFieldTargets on Pet {
+              ... on Dog {
+                name
+              }
+              ... on Cat {
+                name: nickname
+              }
+            }
+            """
+        )
+
+    def alias_masking_direct_field_access():
+        assert_errors(
+            """
+            fragment aliasMaskingDirectFieldAccess on Dog {
+              name: nickname
+              name
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'name' conflict"
+                    " because 'nickname' and 'name' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(3, 15), (4, 15)],
+                }
+            ],
+        )
+
+    def different_args_second_adds_an_argument():
+        assert_errors(
+            """
+            fragment conflictingArgs on Dog {
+              doesKnowCommand
+              doesKnowCommand(dogCommand: HEEL)
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'doesKnowCommand' conflict"
+                    " because they have differing arguments."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(3, 15), (4, 15)],
+                }
+            ],
+        )
+
+    def different_args_second_missing_an_argument():
+        assert_errors(
+            """
+            fragment conflictingArgs on Dog {
+              doesKnowCommand(dogCommand: SIT)
+              doesKnowCommand
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'doesKnowCommand' conflict"
+                    " because they have differing arguments."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(3, 15), (4, 15)],
+                }
+            ],
+        )
+
+    def conflicting_arg_values():
+        assert_errors(
+            """
+            fragment conflictingArgs on Dog {
+              doesKnowCommand(dogCommand: SIT)
+              doesKnowCommand(dogCommand: HEEL)
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'doesKnowCommand' conflict"
+                    " because they have differing arguments."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(3, 15), (4, 15)],
+                }
+            ],
+        )
+
+    def conflicting_arg_names():
+        assert_errors(
+            """
+            fragment conflictingArgs on Dog {
+              isAtLocation(x: 0)
+              isAtLocation(y: 0)
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'isAtLocation' conflict"
+                    " because they have differing arguments."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(3, 15), (4, 15)],
+                }
+            ],
+        )
+
+    def allows_different_args_where_no_conflict_is_possible():
+        # This is valid since no object can be both a "Dog" and a "Cat", thus
+        # these fields can never overlap.
+        assert_valid(
+            """
+            fragment conflictingArgs on Pet {
+              ... on Dog {
+                name(surname: true)
+              }
+              ... on Cat {
+                name
+              }
+            }
+            """
+        )
+
+    def encounters_conflict_in_fragments():
+        assert_errors(
+            """
+            {
+              ...A
+              ...B
+            }
+            fragment A on Type {
+              x: a
+            }
+            fragment B on Type {
+              x: b
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'x' conflict"
+                    " because 'a' and 'b' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(7, 15), (10, 15)],
+                }
+            ],
+        )
+
+    def reports_each_conflict_once():
+        assert_errors(
+            """
+            {
+              f1 {
+                ...A
+                ...B
+              }
+              f2 {
+                ...B
+                ...A
+              }
+              f3 {
+                ...A
+                ...B
+                x: c
+              }
+            }
+            fragment A on Type {
+              x: a
+            }
+            fragment B on Type {
+              x: b
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'x' conflict"
+                    " because 'a' and 'b' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(18, 15), (21, 15)],
+                },
+                {
+                    "message": "Fields 'x' conflict"
+                    " because 'c' and 'a' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(14, 17), (18, 15)],
+                },
+                {
+                    "message": "Fields 'x' conflict"
+                    " because 'c' and 'b' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(14, 17), (21, 15)],
+                },
+            ],
+        )
+
+    def deep_conflict():
+        assert_errors(
+            """
+            {
+              field {
+                x: a
+              },
+              field {
+                x: b
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'field' conflict"
+                    " because subfields 'x' conflict"
+                    " because 'a' and 'b' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(3, 15), (4, 17), (6, 15), (7, 17)],
+                }
+            ],
+        )
+
+    def deep_conflict_with_multiple_issues():
+        assert_errors(
+            """
+            {
+              field {
+                x: a
+                y: c
+              },
+              field {
+                x: b
+                y: d
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'field' conflict"
+                    " because subfields 'x' conflict"
+                    " because 'a' and 'b' are different fields"
+                    " and subfields 'y' conflict"
+                    " because 'c' and 'd' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(3, 15), (4, 17), (5, 17), (7, 15), (8, 17), (9, 17)],
+                    "path": None,
+                }
+            ],
+        )
+
+    def very_deep_conflict():
+        assert_errors(
+            """
+            {
+              field {
+                deepField {
+                  x: a
+                }
+              },
+              field {
+                deepField {
+                  x: b
+                }
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'field' conflict"
+                    " because subfields 'deepField' conflict"
+                    " because subfields 'x' conflict"
+                    " because 'a' and 'b' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [
+                        (3, 15),
+                        (4, 17),
+                        (5, 19),
+                        (8, 15),
+                        (9, 17),
+                        (10, 19),
+                    ],
+                    "path": None,
+                }
+            ],
+        )
+
+    def reports_deep_conflict_to_nearest_common_ancestor():
+        assert_errors(
+            """
+            {
+              field {
+                deepField {
+                  x: a
+                }
+                deepField {
+                  x: b
+                }
+              },
+              field {
+                deepField {
+                  y
+                }
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'deepField' conflict"
+                    " because subfields 'x' conflict"
+                    " because 'a' and 'b' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(4, 17), (5, 19), (7, 17), (8, 19)],
+                }
+            ],
+        )
+
+    def reports_deep_conflict_to_nearest_common_ancestor_in_fragments():
+        assert_errors(
+            """
+            {
+              field {
+                ...F
+              }
+              field {
+                ...F
+              }
+            }
+            fragment F on T {
+              deepField {
+                deeperField {
+                  x: a
+                }
+                deeperField {
+                  x: b
+                }
+              },
+              deepField {
+                deeperField {
+                  y
+                }
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'deeperField' conflict"
+                    " because subfields 'x' conflict"
+                    " because 'a' and 'b' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(12, 17), (13, 19), (15, 17), (16, 19)],
+                }
+            ],
+        )
+
+    def reports_deep_conflict_in_nested_fragments():
+        assert_errors(
+            """
+            {
+              field {
+                ...F
+              },
+              field {
+                ...I
+              }
+            }
+            fragment F on T {
+              x: a
+              ...G
+            }
+            fragment G on T {
+              y: c
+            }
+            fragment I on T {
+              y: d
+              ...J
+            }
+            fragment J on T {
+              x: b
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'field' conflict"
+                    " because subfields 'x' conflict"
+                    " because 'a' and 'b' are different fields"
+                    " and subfields 'y' conflict"
+                    " because 'c' and 'd' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [
+                        (3, 15),
+                        (11, 15),
+                        (15, 15),
+                        (6, 15),
+                        (22, 15),
+                        (18, 15),
+                    ],
+                    "path": None,
+                }
+            ],
+        )
+
+    def ignores_unknown_fragments():
+        assert_valid(
+            """
+            {
+              field
+              ...Unknown
+              ...Known
+            }
+
+            fragment Known on T {
+              field
+              ...OtherUnknown
+            }
+            """
+        )
+
+    def describe_return_types_must_be_unambiguous():
+
+        schema = build_schema(
+            """
+            interface SomeBox {
+              deepBox: SomeBox
+              unrelatedField: String
+            }
+
+            type StringBox implements SomeBox {
+              scalar: String
+              deepBox: StringBox
+              unrelatedField: String
+              listStringBox: [StringBox]
+              stringBox: StringBox
+              intBox: IntBox
+            }
+
+            type IntBox implements SomeBox {
+              scalar: Int
+              deepBox: IntBox
+              unrelatedField: String
+              listStringBox: [StringBox]
+              stringBox: StringBox
+              intBox: IntBox
+            }
+
+            interface NonNullStringBox1 {
+              scalar: String!
+            }
+
+            type NonNullStringBox1Impl implements SomeBox & NonNullStringBox1 {
+              scalar: String!
+              unrelatedField: String
+              deepBox: SomeBox
+            }
+
+            interface NonNullStringBox2 {
+              scalar: String!
+            }
+
+            type NonNullStringBox2Impl implements SomeBox & NonNullStringBox2 {
+              scalar: String!
+              unrelatedField: String
+              deepBox: SomeBox
+            }
+
+            type Connection {
+              edges: [Edge]
+            }
+
+            type Edge {
+              node: Node
+            }
+
+            type Node {
+              id: ID
+              name: String
+            }
+
+            type Query {
+              someBox: SomeBox
+              connection: Connection
+            }
+            """
+        )
+
+        def conflicting_return_types_which_potentially_overlap():
+            # This is invalid since an object could potentially be both the
+            # Object type IntBox and the interface type NonNullStringBox1.
+            # While that condition does not exist in the current schema, the
+            # schema could expand in the future to allow this.
+            assert_errors(
+                """
+                {
+                  someBox {
+                    ...on IntBox {
+                      scalar
+                    }
+                    ...on NonNullStringBox1 {
+                      scalar
+                    }
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'scalar' conflict because"
+                        " they return conflicting types 'Int' and 'String!'."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [(5, 23), (8, 23)],
+                    }
+                ],
+                schema,
+            )
+
+        def compatible_return_shapes_on_different_return_types():
+            # In this case `deepBox` returns `SomeBox` in the first usage, and
+            # `StringBox` in the second usage. These types are not the same!
+            # However this is valid because the return *shapes* are compatible.
+            assert_valid(
+                """
+                {
+                  someBox {
+                      ... on SomeBox {
+                      deepBox {
+                        unrelatedField
+                      }
+                    }
+                    ... on StringBox {
+                      deepBox {
+                        unrelatedField
+                      }
+                    }
+                  }
+                }
+                """,
+                schema=schema,
+            )
+
+        def disallows_differing_return_types_despite_no_overlap():
+            assert_errors(
+                """
+                {
+                  someBox {
+                    ... on IntBox {
+                      scalar
+                    }
+                    ... on StringBox {
+                      scalar
+                    }
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'scalar' conflict because"
+                        " they return conflicting types 'Int' and 'String'."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [(5, 23), (8, 23)],
+                    }
+                ],
+                schema,
+            )
+
+        def reports_correctly_when_a_non_exclusive_follows_an_exclusive():
+            assert_errors(
+                """
+                {
+                  someBox {
+                    ... on IntBox {
+                      deepBox {
+                        ...X
+                      }
+                    }
+                  }
+                  someBox {
+                    ... on StringBox {
+                      deepBox {
+                        ...Y
+                      }
+                    }
+                  }
+                  memoed: someBox {
+                    ... on IntBox {
+                      deepBox {
+                        ...X
+                      }
+                    }
+                  }
+                  memoed: someBox {
+                    ... on StringBox {
+                      deepBox {
+                        ...Y
+                      }
+                    }
+                  }
+                  other: someBox {
+                    ...X
+                  }
+                  other: someBox {
+                    ...Y
+                  }
+                }
+                fragment X on SomeBox {
+                  scalar
+                }
+                fragment Y on SomeBox {
+                  scalar: unrelatedField
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'other' conflict because"
+                        " subfields 'scalar' conflict because"
+                        " 'scalar' and 'unrelatedField' are different fields."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [(31, 19), (39, 19), (34, 19), (42, 19)],
+                        "path": None,
+                    }
+                ],
+                schema,
+            )
+
+        def disallows_differing_return_type_nullability_despite_no_overlap():
+            assert_errors(
+                """
+                {
+                  someBox {
+                    ... on NonNullStringBox1 {
+                      scalar
+                    }
+                    ... on StringBox {
+                      scalar
+                    }
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'scalar' conflict because"
+                        " they return conflicting types 'String!' and 'String'."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [(5, 23), (8, 23)],
+                    }
+                ],
+                schema,
+            )
+
+        def disallows_differing_return_type_list_despite_no_overlap_1():
+            assert_errors(
+                """
+                {
+                  someBox {
+                    ... on IntBox {
+                      box: listStringBox {
+                        scalar
+                      }
+                    }
+                    ... on StringBox {
+                      box: stringBox {
+                        scalar
+                      }
+                    }
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'box' conflict because they return"
+                        " conflicting types '[StringBox]' and 'StringBox'."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [(5, 23), (10, 23)],
+                    }
+                ],
+                schema,
+            )
+
+            assert_errors(
+                """
+                {
+                  someBox {
+                    ... on IntBox {
+                      box: stringBox {
+                        scalar
+                      }
+                    }
+                    ... on StringBox {
+                      box: listStringBox {
+                        scalar
+                      }
+                    }
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'box' conflict because they return"
+                        " conflicting types 'StringBox' and '[StringBox]'."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [(5, 23), (10, 23)],
+                    }
+                ],
+                schema,
+            )
+
+        def disallows_differing_subfields():
+            assert_errors(
+                """
+                {
+                  someBox {
+                    ... on IntBox {
+                      box: stringBox {
+                        val: scalar
+                        val: unrelatedField
+                      }
+                    }
+                    ... on StringBox {
+                      box: stringBox {
+                        val: scalar
+                      }
+                    }
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'val' conflict because"
+                        " 'scalar' and 'unrelatedField' are different fields."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [(6, 25), (7, 25)],
+                    }
+                ],
+                schema,
+            )
+
+        def disallows_differing_deep_return_types_despite_no_overlap():
+            assert_errors(
+                """
+                {
+                  someBox {
+                    ... on IntBox {
+                      box: stringBox {
+                        scalar
+                      }
+                    }
+                    ... on StringBox {
+                      box: intBox {
+                        scalar
+                      }
+                    }
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'box' conflict"
+                        " because subfields 'scalar' conflict"
+                        " because they return conflicting types 'String' and 'Int'."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [(5, 23), (6, 25), (10, 23), (11, 25)],
+                        "path": None,
+                    }
+                ],
+                schema,
+            )
+
+        def allows_non_conflicting_overlapping_types():
+            assert_valid(
+                """
+                {
+                  someBox {
+                    ... on IntBox {
+                      scalar: unrelatedField
+                    }
+                    ... on StringBox {
+                      scalar
+                    }
+                  }
+                }
+                """,
+                schema=schema,
+            )
+
+        def same_wrapped_scalar_return_types():
+            assert_valid(
+                """
+                {
+                  someBox {
+                    ...on NonNullStringBox1 {
+                      scalar
+                    }
+                    ...on NonNullStringBox2 {
+                      scalar
+                    }
+                  }
+                }
+                """,
+                schema=schema,
+            )
+
+        def allows_inline_fragments_without_type_condition():
+            assert_valid(
+                """
+                {
+                  a
+                  ... {
+                    a
+                  }
+                }
+                """,
+                schema=schema,
+            )
+
+        def compares_deep_types_including_list():
+            assert_errors(
+                """
+                {
+                  connection {
+                    ...edgeID
+                    edges {
+                      node {
+                        id: name
+                      }
+                    }
+                  }
+                }
+
+                fragment edgeID on Connection {
+                  edges {
+                    node {
+                      id
+                    }
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Fields 'edges' conflict"
+                        " because subfields 'node' conflict"
+                        " because subfields 'id' conflict"
+                        " because 'name' and 'id' are different fields."
+                        " Use different aliases on the fields"
+                        " to fetch both if this was intentional.",
+                        "locations": [
+                            (5, 21),
+                            (6, 23),
+                            (7, 25),
+                            (14, 19),
+                            (15, 21),
+                            (16, 23),
+                        ],
+                        "path": None,
+                    }
+                ],
+                schema,
+            )
+
+        def ignores_unknown_types():
+            assert_valid(
+                """
+                {
+                  someBox {
+                    ...on UnknownType {
+                      scalar
+                    }
+                    ...on NonNullStringBox2 {
+                      scalar
+                    }
+                  }
+                }
+                """,
+                schema=schema,
+            )
+
+        def works_for_field_names_that_are_js_keywords():
+            schema_with_keywords = build_schema(
+                """
+                type Foo {
+                  constructor: String
+                }
+
+                type Query {
+                  foo: Foo
+                }
+                """
+            )
+
+            assert_valid(
+                """
+                {
+                  foo {
+                    constructor
+                  }
+                }
+                """,
+                schema=schema_with_keywords,
+            )
+
+        def works_for_field_names_that_are_python_keywords():
+            schema_with_keywords = build_schema(
+                """
+                type Foo {
+                  class: String
+                }
+
+                type Query {
+                  foo: Foo
+                }
+                """
+            )
+
+            assert_valid(
+                """
+                {
+                  foo {
+                    class
+                  }
+                }
+                """,
+                schema=schema_with_keywords,
+            )
+
+    def does_not_infinite_loop_on_recursive_fragments():
+        assert_valid(
+            """
+            fragment fragA on Human { name, relatives { name, ...fragA } }
+            """
+        )
+
+    def does_not_infinite_loop_on_immediately_recursive_fragments():
+        assert_valid(
+            """
+            fragment fragA on Human { name, ...fragA }
+            """
+        )
+
+    def does_not_infinite_loop_on_transitively_recursive_fragments():
+        assert_valid(
+            """
+            fragment fragA on Human { name, ...fragB }
+            fragment fragB on Human { name, ...fragC }
+            fragment fragC on Human { name, ...fragA }
+            """
+        )
+
+    def finds_invalid_case_even_with_immediately_recursive_fragment():
+        assert_errors(
+            """
+            fragment sameAliasesWithDifferentFieldTargets on Dog {
+              ...sameAliasesWithDifferentFieldTargets
+              fido: name
+              fido: nickname
+            }
+            """,
+            [
+                {
+                    "message": "Fields 'fido' conflict"
+                    " because 'name' and 'nickname' are different fields."
+                    " Use different aliases on the fields"
+                    " to fetch both if this was intentional.",
+                    "locations": [(4, 15), (5, 15)],
+                }
+            ],
+        )
diff --git a/tests/validation/test_possible_fragment_spreads.py b/tests/validation/test_possible_fragment_spreads.py
new file mode 100644
index 0000000..eccc4f7
--- /dev/null
+++ b/tests/validation/test_possible_fragment_spreads.py
@@ -0,0 +1,282 @@
+from functools import partial
+
+from graphql.validation import PossibleFragmentSpreadsRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, PossibleFragmentSpreadsRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_possible_fragment_spreads():
+    def of_the_same_object():
+        assert_valid(
+            """
+            fragment objectWithinObject on Dog { ...dogFragment }
+            fragment dogFragment on Dog { barkVolume }
+            """
+        )
+
+    def of_the_same_object_inline_fragment():
+        assert_valid(
+            """
+            fragment objectWithinObjectAnon on Dog { ... on Dog { barkVolume } }
+            """
+        )
+
+    def object_into_implemented_interface():
+        assert_valid(
+            """
+            fragment objectWithinInterface on Pet { ...dogFragment }
+            fragment dogFragment on Dog { barkVolume }
+            """
+        )
+
+    def object_into_containing_union():
+        assert_valid(
+            """
+            fragment objectWithinUnion on CatOrDog { ...dogFragment }
+            fragment dogFragment on Dog { barkVolume }
+            """
+        )
+
+    def union_into_contained_object():
+        assert_valid(
+            """
+            fragment unionWithinObject on Dog { ...catOrDogFragment }
+            fragment catOrDogFragment on CatOrDog { __typename }
+            """
+        )
+
+    def union_into_overlapping_interface():
+        assert_valid(
+            """
+            fragment unionWithinInterface on Pet { ...catOrDogFragment }
+            fragment catOrDogFragment on CatOrDog { __typename }
+            """
+        )
+
+    def union_into_overlapping_union():
+        assert_valid(
+            """
+            fragment unionWithinUnion on DogOrHuman { ...catOrDogFragment }
+            fragment catOrDogFragment on CatOrDog { __typename }
+            """
+        )
+
+    def interface_into_implemented_object():
+        assert_valid(
+            """
+            fragment interfaceWithinObject on Dog { ...petFragment }
+            fragment petFragment on Pet { name }
+            """
+        )
+
+    def interface_into_overlapping_interface():
+        assert_valid(
+            """
+            fragment interfaceWithinInterface on Pet { ...beingFragment }
+            fragment beingFragment on Being { name }
+            """
+        )
+
+    def interface_into_overlapping_interface_in_inline_fragment():
+        assert_valid(
+            """
+            fragment interfaceWithinInterface on Pet { ... on Being { name } }
+            """
+        )
+
+    def interface_into_overlapping_union():
+        assert_valid(
+            """
+            fragment interfaceWithinUnion on CatOrDog { ...petFragment }
+            fragment petFragment on Pet { name }
+            """
+        )
+
+    def ignores_incorrect_type_caught_by_fragments_on_composite_types():
+        assert_valid(
+            """
+            fragment petFragment on Pet { ...badInADifferentWay }
+            fragment badInADifferentWay on String { name }
+            """
+        )
+
+    def ignores_unknown_fragments_caught_by_known_fragment_names():
+        assert_valid(
+            """
+            fragment petFragment on Pet { ...UnknownFragment }
+            """
+        )
+
+    def different_object_into_object():
+        assert_errors(
+            """
+            fragment invalidObjectWithinObject on Cat { ...dogFragment }
+            fragment dogFragment on Dog { barkVolume }
+            """,
+            [
+                {
+                    "message": "Fragment 'dogFragment' cannot be spread here"
+                    " as objects of type 'Cat' can never be of type 'Dog'.",
+                    "locations": [(2, 57)],
+                },
+            ],
+        )
+
+    def different_object_into_object_in_inline_fragment():
+        assert_errors(
+            """
+            fragment invalidObjectWithinObjectAnon on Cat {
+              ... on Dog { barkVolume }
+            }
+            """,
+            [
+                {
+                    "message": "Fragment cannot be spread here"
+                    " as objects of type 'Cat' can never be of type 'Dog'.",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def object_into_not_implementing_interface():
+        assert_errors(
+            """
+            fragment invalidObjectWithinInterface on Pet { ...humanFragment }
+            fragment humanFragment on Human { pets { name } }
+            """,
+            [
+                {
+                    "message": "Fragment 'humanFragment' cannot be spread here"
+                    " as objects of type 'Pet' can never be of type 'Human'.",
+                    "locations": [(2, 60)],
+                },
+            ],
+        )
+
+    def object_into_not_containing_union():
+        assert_errors(
+            """
+            fragment invalidObjectWithinUnion on CatOrDog { ...humanFragment }
+            fragment humanFragment on Human { pets { name } }
+            """,
+            [
+                {
+                    "message": "Fragment 'humanFragment' cannot be spread here"
+                    " as objects of type 'CatOrDog' can never be of type 'Human'.",
+                    "locations": [(2, 61)],
+                },
+            ],
+        )
+
+    def union_into_not_contained_object():
+        assert_errors(
+            """
+            fragment invalidUnionWithinObject on Human { ...catOrDogFragment }
+            fragment catOrDogFragment on CatOrDog { __typename }
+            """,
+            [
+                {
+                    "message": "Fragment 'catOrDogFragment' cannot be spread here"
+                    " as objects of type 'Human' can never be of type 'CatOrDog'.",
+                    "locations": [(2, 58)],
+                },
+            ],
+        )
+
+    def union_into_non_overlapping_interface():
+        assert_errors(
+            """
+            fragment invalidUnionWithinInterface on Pet { ...humanOrAlienFragment }
+            fragment humanOrAlienFragment on HumanOrAlien { __typename }
+            """,
+            [
+                {
+                    "message": "Fragment 'humanOrAlienFragment' cannot be spread here"
+                    " as objects of type 'Pet' can never be of type 'HumanOrAlien'.",
+                    "locations": [(2, 59)],
+                },
+            ],
+        )
+
+    def union_into_non_overlapping_union():
+        assert_errors(
+            """
+            fragment invalidUnionWithinUnion on CatOrDog { ...humanOrAlienFragment }
+            fragment humanOrAlienFragment on HumanOrAlien { __typename }
+            """,
+            [
+                {
+                    "message": "Fragment 'humanOrAlienFragment'"
+                    " cannot be spread here as objects of type 'CatOrDog'"
+                    " can never be of type 'HumanOrAlien'.",
+                    "locations": [(2, 60)],
+                },
+            ],
+        )
+
+    def interface_into_non_implementing_object():
+        assert_errors(
+            """
+            fragment invalidInterfaceWithinObject on Cat { ...intelligentFragment }
+            fragment intelligentFragment on Intelligent { iq }
+            """,
+            [
+                {
+                    "message": "Fragment 'intelligentFragment' cannot be spread here"
+                    " as objects of type 'Cat' can never be of type 'Intelligent'.",
+                    "locations": [(2, 60)],
+                },
+            ],
+        )
+
+    def interface_into_non_overlapping_interface():
+        assert_errors(
+            """
+            fragment invalidInterfaceWithinInterface on Pet {
+              ...intelligentFragment
+            }
+            fragment intelligentFragment on Intelligent { iq }
+            """,
+            [
+                {
+                    "message": "Fragment 'intelligentFragment' cannot be spread here"
+                    " as objects of type 'Pet' can never be of type 'Intelligent'.",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def interface_into_non_overlapping_interface_in_inline_fragment():
+        assert_errors(
+            """
+            fragment invalidInterfaceWithinInterfaceAnon on Pet {
+              ...on Intelligent { iq }
+            }
+            """,
+            [
+                {
+                    "message": "Fragment cannot be spread here as objects"
+                    " of type 'Pet' can never be of type 'Intelligent'.",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def interface_into_non_overlapping_union():
+        assert_errors(
+            """
+            fragment invalidInterfaceWithinUnion on HumanOrAlien { ...petFragment }
+            fragment petFragment on Pet { name }
+            """,
+            [
+                {
+                    "message": "Fragment 'petFragment' cannot be spread here"
+                    " as objects of type 'HumanOrAlien' can never be of type 'Pet'.",
+                    "locations": [(2, 68)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_possible_type_extensions.py b/tests/validation/test_possible_type_extensions.py
new file mode 100644
index 0000000..473e0c8
--- /dev/null
+++ b/tests/validation/test_possible_type_extensions.py
@@ -0,0 +1,268 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation.rules.possible_type_extensions import PossibleTypeExtensionsRule
+
+from .harness import assert_sdl_validation_errors
+
+assert_errors = partial(assert_sdl_validation_errors, PossibleTypeExtensionsRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_possible_type_extensions():
+    def no_extensions():
+        assert_valid(
+            """
+            scalar FooScalar
+            type FooObject
+            interface FooInterface
+            union FooUnion
+            enum FooEnum
+            input FooInputObject
+            """
+        )
+
+    def one_extension_per_type():
+        assert_valid(
+            """
+            scalar FooScalar
+            type FooObject
+            interface FooInterface
+            union FooUnion
+            enum FooEnum
+            input FooInputObject
+
+            extend scalar FooScalar @dummy
+            extend type FooObject @dummy
+            extend interface FooInterface @dummy
+            extend union FooUnion @dummy
+            extend enum FooEnum @dummy
+            extend input FooInputObject @dummy
+            """
+        )
+
+    def many_extensions_per_type():
+        assert_valid(
+            """
+            scalar FooScalar
+            type FooObject
+            interface FooInterface
+            union FooUnion
+            enum FooEnum
+            input FooInputObject
+
+            extend scalar FooScalar @dummy
+            extend type FooObject @dummy
+            extend interface FooInterface @dummy
+            extend union FooUnion @dummy
+            extend enum FooEnum @dummy
+            extend input FooInputObject @dummy
+
+            extend scalar FooScalar @dummy
+            extend type FooObject @dummy
+            extend interface FooInterface @dummy
+            extend union FooUnion @dummy
+            extend enum FooEnum @dummy
+            extend input FooInputObject @dummy
+            """
+        )
+
+    def extending_unknown_type():
+        message = (
+            "Cannot extend type 'Unknown' because it is not defined."
+            " Did you mean 'Known'?"
+        )
+
+        assert_errors(
+            """
+            type Known
+
+            extend scalar Unknown @dummy
+            extend type Unknown @dummy
+            extend interface Unknown @dummy
+            extend union Unknown @dummy
+            extend enum Unknown @dummy
+            extend input Unknown @dummy
+            """,
+            [
+                {"message": message, "locations": [(4, 27)]},
+                {"message": message, "locations": [(5, 25)]},
+                {"message": message, "locations": [(6, 30)]},
+                {"message": message, "locations": [(7, 26)]},
+                {"message": message, "locations": [(8, 25)]},
+                {"message": message, "locations": [(9, 26)]},
+            ],
+        )
+
+    def does_not_consider_non_type_definitions():
+        message = "Cannot extend type 'Foo' because it is not defined."
+
+        assert_errors(
+            """
+            query Foo { __typename }
+            fragment Foo on Query { __typename }
+            directive @Foo on SCHEMA
+
+            extend scalar Foo @dummy
+            extend type Foo @dummy
+            extend interface Foo @dummy
+            extend union Foo @dummy
+            extend enum Foo @dummy
+            extend input Foo @dummy
+            """,
+            [
+                {"message": message, "locations": [(6, 27)]},
+                {"message": message, "locations": [(7, 25)]},
+                {"message": message, "locations": [(8, 30)]},
+                {"message": message, "locations": [(9, 26)]},
+                {"message": message, "locations": [(10, 25)]},
+                {"message": message, "locations": [(11, 26)]},
+            ],
+        )
+
+    def extending_with_different_kinds():
+        assert_errors(
+            """
+            scalar FooScalar
+            type FooObject
+            interface FooInterface
+            union FooUnion
+            enum FooEnum
+            input FooInputObject
+
+            extend type FooScalar @dummy
+            extend interface FooObject @dummy
+            extend union FooInterface @dummy
+            extend enum FooUnion @dummy
+            extend input FooEnum @dummy
+            extend scalar FooInputObject @dummy
+            """,
+            [
+                {
+                    "message": "Cannot extend non-object type 'FooScalar'.",
+                    "locations": [(2, 13), (9, 13)],
+                },
+                {
+                    "message": "Cannot extend non-interface type 'FooObject'.",
+                    "locations": [(3, 13), (10, 13)],
+                },
+                {
+                    "message": "Cannot extend non-union type 'FooInterface'.",
+                    "locations": [(4, 13), (11, 13)],
+                },
+                {
+                    "message": "Cannot extend non-enum type 'FooUnion'.",
+                    "locations": [(5, 13), (12, 13)],
+                },
+                {
+                    "message": "Cannot extend non-input object type 'FooEnum'.",
+                    "locations": [(6, 13), (13, 13)],
+                },
+                {
+                    "message": "Cannot extend non-scalar type 'FooInputObject'.",
+                    "locations": [(7, 13), (14, 13)],
+                },
+            ],
+        )
+
+    def extending_types_within_existing_schema():
+        schema = build_schema(
+            """
+            scalar FooScalar
+            type FooObject
+            interface FooInterface
+            union FooUnion
+            enum FooEnum
+            input FooInputObject
+            """
+        )
+        sdl = """
+            extend scalar FooScalar @dummy
+            extend type FooObject @dummy
+            extend interface FooInterface @dummy
+            extend union FooUnion @dummy
+            extend enum FooEnum @dummy
+            extend input FooInputObject @dummy
+            """
+
+        assert_valid(sdl, schema=schema)
+
+    def extending_unknown_types_within_existing_schema():
+        schema = build_schema("type Known")
+        sdl = """
+            extend scalar Unknown @dummy
+            extend type Unknown @dummy
+            extend interface Unknown @dummy
+            extend union Unknown @dummy
+            extend enum Unknown @dummy
+            extend input Unknown @dummy
+            """
+
+        message = (
+            "Cannot extend type 'Unknown' because it is not defined."
+            " Did you mean 'Known'?"
+        )
+        assert_errors(
+            sdl,
+            [
+                {"message": message, "locations": [(2, 27)]},
+                {"message": message, "locations": [(3, 25)]},
+                {"message": message, "locations": [(4, 30)]},
+                {"message": message, "locations": [(5, 26)]},
+                {"message": message, "locations": [(6, 25)]},
+                {"message": message, "locations": [(7, 26)]},
+            ],
+            schema,
+        )
+
+    def extending_types_with_different_kinds_within_existing_schema():
+        schema = build_schema(
+            """
+            scalar FooScalar
+            type FooObject
+            interface FooInterface
+            union FooUnion
+            enum FooEnum
+            input FooInputObject
+            """
+        )
+        sdl = """
+            extend type FooScalar @dummy
+            extend interface FooObject @dummy
+            extend union FooInterface @dummy
+            extend enum FooUnion @dummy
+            extend input FooEnum @dummy
+            extend scalar FooInputObject @dummy
+            """
+
+        assert_errors(
+            sdl,
+            [
+                {
+                    "message": "Cannot extend non-object type 'FooScalar'.",
+                    "locations": [(2, 13)],
+                },
+                {
+                    "message": "Cannot extend non-interface type 'FooObject'.",
+                    "locations": [(3, 13)],
+                },
+                {
+                    "message": "Cannot extend non-union type 'FooInterface'.",
+                    "locations": [(4, 13)],
+                },
+                {
+                    "message": "Cannot extend non-enum type 'FooUnion'.",
+                    "locations": [(5, 13)],
+                },
+                {
+                    "message": "Cannot extend non-input object type 'FooEnum'.",
+                    "locations": [(6, 13)],
+                },
+                {
+                    "message": "Cannot extend non-scalar type 'FooInputObject'.",
+                    "locations": [(7, 13)],
+                },
+            ],
+            schema,
+        )
diff --git a/tests/validation/test_provided_required_arguments.py b/tests/validation/test_provided_required_arguments.py
new file mode 100644
index 0000000..86bb523
--- /dev/null
+++ b/tests/validation/test_provided_required_arguments.py
@@ -0,0 +1,368 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation import ProvidedRequiredArgumentsRule
+from graphql.validation.rules.provided_required_arguments import (
+    ProvidedRequiredArgumentsOnDirectivesRule,
+)
+
+from .harness import assert_validation_errors, assert_sdl_validation_errors
+
+assert_errors = partial(assert_validation_errors, ProvidedRequiredArgumentsRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+assert_sdl_errors = partial(
+    assert_sdl_validation_errors, ProvidedRequiredArgumentsOnDirectivesRule
+)
+
+assert_sdl_valid = partial(assert_sdl_errors, errors=[])
+
+
+def describe_validate_provided_required_arguments():
+    def ignores_unknown_arguments():
+        assert_valid(
+            """
+            {
+              dog {
+                isHouseTrained(unknownArgument: true)
+              }
+            }"""
+        )
+
+    def describe_valid_non_nullable_value():
+        def arg_on_optional_arg():
+            assert_valid(
+                """
+                {
+                  dog {
+                    isHouseTrained(atOtherHomes: true)
+                  }
+                }"""
+            )
+
+        def no_arg_on_optional_arg():
+            assert_valid(
+                """
+                {
+                  dog {
+                    isHouseTrained
+                  }
+                }"""
+            )
+
+        def no_arg_on_non_null_field_with_default():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    nonNullFieldWithDefault
+                  }
+                }"""
+            )
+
+        def multiple_args():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req1: 1, req2: 2)
+                  }
+                }
+                """
+            )
+
+        def multiple_args_reverse_order():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req2: 2, req1: 1)
+                  }
+                }
+                """
+            )
+
+        def no_args_on_multiple_optional():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOpts
+                  }
+                }
+                """
+            )
+
+        def one_arg_on_multiple_optional():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOpts(opt1: 1)
+                  }
+                }
+                """
+            )
+
+        def second_arg_on_multiple_optional():
+            assert_valid(
+                """
+                {
+                    complicatedArgs {
+                        multipleOpts(opt2: 1)
+                    }
+                }
+                """
+            )
+
+        def multiple_required_args_on_mixed_list():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOptAndReq(req1: 3, req2: 4)
+                  }
+                }
+                """
+            )
+
+        def multiple_required_and_one_optional_arg_on_mixed_list():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
+                  }
+                }
+                """
+            )
+
+        def all_required_and_optional_args_on_mixed_list():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
+                  }
+                }
+                """
+            )
+
+    def describe_invalid_non_nullable_value():
+        def missing_one_non_nullable_argument():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req2: 2)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Field 'multipleReqs' argument 'req1'"
+                        " of type 'Int!' is required, but it was not provided.",
+                        "locations": [(4, 21)],
+                    },
+                ],
+            )
+
+        def missing_multiple_non_nullable_arguments():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Field 'multipleReqs' argument 'req1'"
+                        " of type 'Int!' is required, but it was not provided.",
+                        "locations": [(4, 21)],
+                    },
+                    {
+                        "message": "Field 'multipleReqs' argument 'req2'"
+                        " of type 'Int!' is required, but it was not provided.",
+                        "locations": [(4, 21)],
+                    },
+                ],
+            )
+
+        def incorrect_value_and_missing_argument():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req1: "one")
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Field 'multipleReqs' argument 'req2'"
+                        " of type 'Int!' is required, but it was not provided.",
+                        "locations": [(4, 21)],
+                    },
+                ],
+            )
+
+    def describe_directive_arguments():
+        def ignores_unknown_directives():
+            assert_valid(
+                """
+                {
+                  dog @unknown
+                }
+                """
+            )
+
+        def with_directives_of_valid_type():
+            assert_valid(
+                """
+                {
+                  dog @include(if: true) {
+                    name
+                  }
+                  human @skip(if: false) {
+                    name
+                  }
+                }
+                """
+            )
+
+        def with_directive_with_missing_types():
+            assert_errors(
+                """
+                {
+                  dog @include {
+                    name @skip
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Directive '@include' argument 'if' of type"
+                        " 'Boolean!' is required, but it was not provided.",
+                        "locations": [(3, 23)],
+                    },
+                    {
+                        "message": "Directive '@skip' argument 'if' of type"
+                        " 'Boolean!' is required, but it was not provided.",
+                        "locations": [(4, 26)],
+                    },
+                ],
+            )
+
+    def describe_within_sdl():
+        def missing_optional_args_on_directive_defined_inside_sdl():
+            assert_sdl_valid(
+                """
+                type Query {
+                foo: String @test
+                }
+
+                directive @test(arg1: String, arg2: String! = "") on FIELD_DEFINITION
+                """
+            )
+
+        def missing_arg_on_directive_defined_inside_sdl():
+            assert_sdl_errors(
+                """
+                type Query {
+                  foo: String @test
+                }
+
+                directive @test(arg: String!) on FIELD_DEFINITION
+                """,
+                [
+                    {
+                        "message": "Directive '@test' argument 'arg' of type"
+                        " 'String!' is required, but it was not provided.",
+                        "locations": [(3, 31)],
+                    },
+                ],
+            )
+
+        def missing_arg_on_standard_directive():
+            assert_sdl_errors(
+                """
+                type Query {
+                  foo: String @include
+                }
+                """,
+                [
+                    {
+                        "message": "Directive '@include' argument 'if' of type"
+                        " 'Boolean!' is required, but it was not provided.",
+                        "locations": [(3, 31)],
+                    },
+                ],
+            )
+
+        def missing_arg_on_overridden_standard_directive():
+            assert_sdl_errors(
+                """
+                type Query {
+                  foo: String @deprecated
+                }
+                directive @deprecated(reason: String!) on FIELD
+                """,
+                [
+                    {
+                        "message": "Directive '@deprecated' argument 'reason' of type"
+                        " 'String!' is required, but it was not provided.",
+                        "locations": [(3, 31)],
+                    },
+                ],
+            )
+
+        def missing_arg_on_directive_defined_in_schema_extension():
+            schema = build_schema(
+                """
+                type Query {
+                  foo: String
+                }
+                """
+            )
+            assert_sdl_errors(
+                """
+                directive @test(arg: String!) on OBJECT
+
+                extend type Query  @test
+                """,
+                [
+                    {
+                        "message": "Directive '@test' argument 'arg' of type"
+                        " 'String!' is required, but it was not provided.",
+                        "locations": [(4, 36)],
+                    },
+                ],
+                schema,
+            )
+
+        def missing_arg_on_directive_used_in_schema_extension():
+            schema = build_schema(
+                """
+                directive @test(arg: String!) on OBJECT
+
+                type Query {
+                  foo: String
+                }
+                """
+            )
+            assert_sdl_errors(
+                """
+                extend type Query  @test
+                """,
+                [
+                    {
+                        "message": "Directive '@test' argument 'arg' of type"
+                        " 'String!' is required, but it was not provided.",
+                        "locations": [(2, 36)],
+                    },
+                ],
+                schema,
+            )
diff --git a/tests/validation/test_scalar_leafs.py b/tests/validation/test_scalar_leafs.py
new file mode 100644
index 0000000..5d7b83d
--- /dev/null
+++ b/tests/validation/test_scalar_leafs.py
@@ -0,0 +1,143 @@
+from functools import partial
+
+from graphql.validation import ScalarLeafsRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, ScalarLeafsRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_scalar_leafs():
+    def valid_scalar_selection():
+        assert_valid(
+            """
+            fragment scalarSelection on Dog {
+              barks
+            }
+            """
+        )
+
+    def object_type_missing_selection():
+        assert_errors(
+            """
+            query directQueryOnObjectWithoutSubFields {
+              human
+            }
+            """,
+            [
+                {
+                    "message": "Field 'human' of type 'Human'"
+                    " must have a selection of subfields."
+                    " Did you mean 'human { ... }'?",
+                    "locations": [(3, 15)],
+                },
+            ],
+        )
+
+    def interface_type_missing_selection():
+        assert_errors(
+            """
+            {
+              human { pets }
+            }
+            """,
+            [
+                {
+                    "message": "Field 'pets' of type '[Pet]'"
+                    " must have a selection of subfields."
+                    " Did you mean 'pets { ... }'?",
+                    "locations": [(3, 23)],
+                },
+            ],
+        )
+
+    def valid_scalar_selection_with_args():
+        assert_valid(
+            """
+            fragment scalarSelectionWithArgs on Dog {
+              doesKnowCommand(dogCommand: SIT)
+            }
+            """
+        )
+
+    def scalar_selection_not_allowed_on_boolean():
+        assert_errors(
+            """
+            fragment scalarSelectionsNotAllowedOnBoolean on Dog {
+              barks { sinceWhen }
+            }
+            """,
+            [
+                {
+                    "message": "Field 'barks' must not have a selection"
+                    " since type 'Boolean' has no subfields.",
+                    "locations": [(3, 21)],
+                },
+            ],
+        )
+
+    def scalar_selection_not_allowed_on_enum():
+        assert_errors(
+            """
+            fragment scalarSelectionsNotAllowedOnEnum on Cat {
+              furColor { inHexDec }
+            }
+            """,
+            [
+                {
+                    "message": "Field 'furColor' must not have a selection"
+                    " since type 'FurColor' has no subfields.",
+                    "locations": [(3, 24)],
+                },
+            ],
+        )
+
+    def scalar_selection_not_allowed_with_args():
+        assert_errors(
+            """
+            fragment scalarSelectionsNotAllowedWithArgs on Dog {
+              doesKnowCommand(dogCommand: SIT) { sinceWhen }
+            }
+            """,
+            [
+                {
+                    "message": "Field 'doesKnowCommand' must not have a selection"
+                    " since type 'Boolean' has no subfields.",
+                    "locations": [(3, 48)],
+                },
+            ],
+        )
+
+    def scalar_selection_not_allowed_with_directives():
+        assert_errors(
+            """
+            fragment scalarSelectionsNotAllowedWithDirectives on Dog {
+              name @include(if: true) { isAlsoHumanName }
+            }
+            """,
+            [
+                {
+                    "message": "Field 'name' must not have a selection"
+                    " since type 'String' has no subfields.",
+                    "locations": [(3, 39)],
+                },
+            ],
+        )
+
+    def scalar_selection_not_allowed_with_directives_and_args():
+        assert_errors(
+            """
+            fragment scalarSelectionsNotAllowedWithDirectivesAndArgs on Dog {
+              doesKnowCommand(dogCommand: SIT) @include(if: true) { sinceWhen }
+            }
+            """,
+            [
+                {
+                    "message": "Field 'doesKnowCommand' must not have a selection"
+                    " since type 'Boolean' has no subfields.",
+                    "locations": [(3, 67)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_single_field_subscriptions.py b/tests/validation/test_single_field_subscriptions.py
new file mode 100644
index 0000000..3bc4e98
--- /dev/null
+++ b/tests/validation/test_single_field_subscriptions.py
@@ -0,0 +1,89 @@
+from functools import partial
+
+from graphql.validation import SingleFieldSubscriptionsRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, SingleFieldSubscriptionsRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_subscriptions_with_single_field():
+    def valid_subscription():
+        assert_valid(
+            """
+            subscription ImportantEmails {
+              importantEmails
+            }
+            """
+        )
+
+    def fails_with_more_than_one_root_field():
+        assert_errors(
+            """
+            subscription ImportantEmails {
+              importantEmails
+              notImportantEmails
+            }
+            """,
+            [
+                {
+                    "message": "Subscription 'ImportantEmails'"
+                    " must select only one top level field.",
+                    "locations": [(4, 15)],
+                }
+            ],
+        )
+
+    def fails_with_more_than_one_root_field_including_introspection():
+        assert_errors(
+            """
+            subscription ImportantEmails {
+              importantEmails
+              __typename
+            }
+            """,
+            [
+                {
+                    "message": "Subscription 'ImportantEmails'"
+                    " must select only one top level field.",
+                    "locations": [(4, 15)],
+                }
+            ],
+        )
+
+    def fails_with_many_more_than_one_root_field():
+        assert_errors(
+            """
+            subscription ImportantEmails {
+              importantEmails
+              notImportantEmails
+              spamEmails
+            }
+            """,
+            [
+                {
+                    "message": "Subscription 'ImportantEmails'"
+                    " must select only one top level field.",
+                    "locations": [(4, 15), (5, 15)],
+                }
+            ],
+        )
+
+    def fails_with_more_than_one_root_field_in_anonymous_subscriptions():
+        assert_errors(
+            """
+            subscription {
+              importantEmails
+              notImportantEmails
+            }
+            """,
+            [
+                {
+                    "message": "Anonymous Subscription"
+                    " must select only one top level field.",
+                    "locations": [(4, 15)],
+                }
+            ],
+        )
diff --git a/tests/validation/test_unique_argument_names.py b/tests/validation/test_unique_argument_names.py
new file mode 100644
index 0000000..dffadfc
--- /dev/null
+++ b/tests/validation/test_unique_argument_names.py
@@ -0,0 +1,160 @@
+from functools import partial
+from graphql.validation import UniqueArgumentNamesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, UniqueArgumentNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_argument_names():
+    def no_arguments_on_field():
+        assert_valid(
+            """
+            {
+              field
+            }
+            """
+        )
+
+    def no_arguments_on_directive():
+        assert_valid(
+            """
+            {
+              field
+            }
+            """
+        )
+
+    def argument_on_field():
+        assert_valid(
+            """
+            {
+              field(arg: "value")
+            }
+            """
+        )
+
+    def argument_on_directive():
+        assert_valid(
+            """
+            {
+              field @directive(arg: "value")
+            }
+            """
+        )
+
+    def same_argument_on_two_fields():
+        assert_valid(
+            """
+            {
+              one: field(arg: "value")
+              two: field(arg: "value")
+            }
+            """
+        )
+
+    def same_argument_on_field_and_directive():
+        assert_valid(
+            """
+            {
+              field(arg: "value") @directive(arg: "value")
+            }
+            """
+        )
+
+    def same_argument_on_two_directives():
+        assert_valid(
+            """
+            {
+              field @directive1(arg: "value") @directive2(arg: "value")
+            }
+            """
+        )
+
+    def multiple_field_arguments():
+        assert_valid(
+            """
+            {
+              field(arg1: "value", arg2: "value", arg3: "value")
+            }
+            """
+        )
+
+    def multiple_directive_arguments():
+        assert_valid(
+            """
+            {
+              field @directive(arg1: "value", arg2: "value", arg3: "value")
+            }
+            """
+        )
+
+    def duplicate_field_arguments():
+        assert_errors(
+            """
+            {
+              field(arg1: "value", arg1: "value")
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one argument named 'arg1'.",
+                    "locations": [(3, 21), (3, 36)],
+                },
+            ],
+        )
+
+    def many_duplicate_field_arguments():
+        assert_errors(
+            """
+            {
+              field(arg1: "value", arg1: "value", arg1: "value")
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one argument named 'arg1'.",
+                    "locations": [(3, 21), (3, 36)],
+                },
+                {
+                    "message": "There can be only one argument named 'arg1'.",
+                    "locations": [(3, 21), (3, 51)],
+                },
+            ],
+        )
+
+    def duplicate_directive_arguments():
+        assert_errors(
+            """
+            {
+              field @directive(arg1: "value", arg1: "value")
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one argument named 'arg1'.",
+                    "locations": [(3, 32), (3, 47)],
+                },
+            ],
+        )
+
+    def many_duplicate_directive_arguments():
+        assert_errors(
+            """
+            {
+              field @directive(arg1: "value", arg1: "value", arg1: "value")
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one argument named 'arg1'.",
+                    "locations": [(3, 32), (3, 47)],
+                },
+                {
+                    "message": "There can be only one argument named 'arg1'.",
+                    "locations": [(3, 32), (3, 62)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_unique_directive_names.py b/tests/validation/test_unique_directive_names.py
new file mode 100644
index 0000000..1cb9dc5
--- /dev/null
+++ b/tests/validation/test_unique_directive_names.py
@@ -0,0 +1,101 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation.rules.unique_directive_names import UniqueDirectiveNamesRule
+
+from .harness import assert_sdl_validation_errors
+
+assert_errors = partial(assert_sdl_validation_errors, UniqueDirectiveNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_directive_names():
+    def no_directive():
+        assert_valid(
+            """
+            type Foo
+            """
+        )
+
+    def one_directive():
+        assert_valid(
+            """
+            directive @foo on SCHEMA
+            """
+        )
+
+    def many_directives():
+        assert_valid(
+            """
+            directive @foo on SCHEMA
+            directive @bar on SCHEMA
+            directive @baz on SCHEMA
+            """
+        )
+
+    def directive_and_non_directive_definitions_named_the_same():
+        assert_valid(
+            """
+            query foo { __typename }
+            fragment foo on foo { __typename }
+            type foo
+
+            directive @foo on SCHEMA
+            """
+        )
+
+    def directives_named_the_same():
+        assert_errors(
+            """
+            directive @foo on SCHEMA
+
+            directive @foo on SCHEMA
+            """,
+            [
+                {
+                    "message": "There can be only one directive named '@foo'.",
+                    "locations": [(2, 24), (4, 24)],
+                }
+            ],
+        )
+
+    def adding_new_directive_to_existing_schema():
+        schema = build_schema("directive @foo on SCHEMA")
+
+        assert_valid("directive @bar on SCHEMA", schema=schema)
+
+    def adding_new_directive_with_standard_name_to_existing_schema():
+        schema = build_schema("type foo")
+
+        assert_errors(
+            "directive @skip on SCHEMA",
+            [
+                {
+                    "message": "Directive '@skip' already exists in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(1, 12)],
+                }
+            ],
+            schema,
+        )
+
+    def adding_new_directive_to_existing_schema_with_same_named_type():
+        schema = build_schema("type foo")
+
+        assert_valid("directive @foo on SCHEMA", schema=schema)
+
+    def adding_conflicting_directives_to_existing_schema():
+        schema = build_schema("directive @foo on SCHEMA")
+
+        assert_errors(
+            "directive @foo on SCHEMA",
+            [
+                {
+                    "message": "Directive '@foo' already exists in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(1, 12)],
+                }
+            ],
+            schema,
+        )
diff --git a/tests/validation/test_unique_directives_per_location.py b/tests/validation/test_unique_directives_per_location.py
new file mode 100644
index 0000000..2984208
--- /dev/null
+++ b/tests/validation/test_unique_directives_per_location.py
@@ -0,0 +1,333 @@
+from functools import partial
+
+from graphql.language import parse
+from graphql.utilities import extend_schema
+from graphql.validation import UniqueDirectivesPerLocationRule
+
+from .harness import assert_validation_errors, assert_sdl_validation_errors, test_schema
+
+extension_sdl = """
+  directive @directive on FIELD | FRAGMENT_DEFINITION
+  directive @directiveA on FIELD | FRAGMENT_DEFINITION
+  directive @directiveB on FIELD | FRAGMENT_DEFINITION
+  directive @repeatable repeatable on FIELD | FRAGMENT_DEFINITION
+"""
+schema_with_directives = extend_schema(test_schema, parse(extension_sdl))
+
+assert_errors = partial(
+    assert_validation_errors,
+    UniqueDirectivesPerLocationRule,
+    schema=schema_with_directives,
+)
+
+assert_valid = partial(assert_errors, errors=[])
+
+assert_sdl_errors = partial(
+    assert_sdl_validation_errors, UniqueDirectivesPerLocationRule
+)
+
+
+def describe_validate_directives_are_unique_per_location():
+    def no_directives():
+        assert_valid(
+            """
+            {
+              field
+            }
+            """
+        )
+
+    def unique_directives_in_different_locations():
+        assert_valid(
+            """
+            fragment Test on Type @directiveA {
+              field @directiveB
+            }
+            """
+        )
+
+    def unique_directives_in_same_locations():
+        assert_valid(
+            """
+            fragment Test on Type @directiveA @directiveB {
+              field @directiveA @directiveB
+            }
+            """
+        )
+
+    def same_directives_in_different_locations():
+        assert_valid(
+            """
+            fragment Test on Type @directiveA {
+              field @directiveA
+            }
+            """
+        )
+
+    def same_directives_in_similar_locations():
+        assert_valid(
+            """
+            fragment Test on Type {
+              field @directive
+              field @directive
+            }
+            """
+        )
+
+    def repeatable_directives_in_same_location():
+        assert_valid(
+            """
+            fragment Test on Type @repeatable @repeatable {
+              field @repeatable @repeatable
+            }
+            """
+        )
+
+    def unknown_directives_must_be_ignored():
+        assert_valid(
+            """
+            type Test @unknown @unknown {
+              field: String! @unknown @unknown
+            }
+
+            extend type Test @unknown {
+              anotherField: String!
+            }
+            """
+        )
+
+    def duplicate_directives_in_one_location():
+        assert_errors(
+            """
+            fragment Test on Type {
+              field @directive @directive
+            }
+            """,
+            [
+                {
+                    "message": "The directive '@directive'"
+                    " can only be used once at this location.",
+                    "locations": [(3, 21), (3, 32)],
+                },
+            ],
+        )
+
+    def many_duplicate_directives_in_one_location():
+        assert_errors(
+            """
+            fragment Test on Type {
+              field @directive @directive @directive
+            }
+            """,
+            [
+                {
+                    "message": "The directive '@directive'"
+                    " can only be used once at this location.",
+                    "locations": [(3, 21), (3, 32)],
+                },
+                {
+                    "message": "The directive '@directive'"
+                    " can only be used once at this location.",
+                    "locations": [(3, 21), (3, 43)],
+                },
+            ],
+        )
+
+    def different_duplicate_directives_in_one_location():
+        assert_errors(
+            """
+            fragment Test on Type {
+              field @directiveA @directiveB @directiveA @directiveB
+            }
+            """,
+            [
+                {
+                    "message": "The directive '@directiveA'"
+                    " can only be used once at this location.",
+                    "locations": [(3, 21), (3, 45)],
+                },
+                {
+                    "message": "The directive '@directiveB'"
+                    " can only be used once at this location.",
+                    "locations": [(3, 33), (3, 57)],
+                },
+            ],
+        )
+
+    def different_duplicate_directives_in_many_locations():
+        assert_errors(
+            """
+            fragment Test on Type @directive @directive {
+              field @directive @directive
+            }
+            """,
+            [
+                {
+                    "message": "The directive '@directive'"
+                    " can only be used once at this location.",
+                    "locations": [(2, 35), (2, 46)],
+                },
+                {
+                    "message": "The directive '@directive'"
+                    " can only be used once at this location.",
+                    "locations": [(3, 21), (3, 32)],
+                },
+            ],
+        )
+
+    def duplicate_directives_on_sdl_definitions():
+        assert_sdl_errors(
+            """
+            directive @nonRepeatable on
+              SCHEMA | SCALAR | OBJECT | INTERFACE | UNION | INPUT_OBJECT
+
+            schema @nonRepeatable @nonRepeatable { query: Dummy }
+
+            scalar TestScalar @nonRepeatable @nonRepeatable
+            type TestObject @nonRepeatable @nonRepeatable
+            interface TestInterface @nonRepeatable @nonRepeatable
+            union TestUnion @nonRepeatable @nonRepeatable
+            input TestInput @nonRepeatable @nonRepeatable
+            """,
+            [
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(5, 20), (5, 35)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(7, 31), (7, 46)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(8, 29), (8, 44)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(9, 37), (9, 52)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(10, 29), (10, 44)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(11, 29), (11, 44)],
+                },
+            ],
+        )
+
+    def duplicate_directives_on_sdl_extensions():
+        assert_sdl_errors(
+            """
+            directive @nonRepeatable on
+              SCHEMA | SCALAR | OBJECT | INTERFACE | UNION | INPUT_OBJECT
+
+            extend schema @nonRepeatable @nonRepeatable
+
+            extend scalar TestScalar @nonRepeatable @nonRepeatable
+            extend type TestObject @nonRepeatable @nonRepeatable
+            extend interface TestInterface @nonRepeatable @nonRepeatable
+            extend union TestUnion @nonRepeatable @nonRepeatable
+            extend input TestInput @nonRepeatable @nonRepeatable
+            """,
+            [
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(5, 27), (5, 42)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(7, 38), (7, 53)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(8, 36), (8, 51)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(9, 44), (9, 59)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(10, 36), (10, 51)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(11, 36), (11, 51)],
+                },
+            ],
+        )
+
+    def duplicate_directives_between_sdl_definitions_and_extensions():
+        assert_sdl_errors(
+            """
+            directive @nonRepeatable on SCHEMA
+
+            schema @nonRepeatable { query: Dummy }
+            extend schema @nonRepeatable
+            """,
+            [
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(4, 20), (5, 27)],
+                },
+            ],
+        )
+
+        assert_sdl_errors(
+            """
+            directive @nonRepeatable on SCALAR
+
+            scalar TestScalar @nonRepeatable
+            extend scalar TestScalar @nonRepeatable
+            scalar TestScalar @nonRepeatable
+            """,
+            [
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(4, 31), (5, 38)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(4, 31), (6, 31)],
+                },
+            ],
+        )
+
+        assert_sdl_errors(
+            """
+            directive @nonRepeatable on OBJECT
+
+            extend type TestObject @nonRepeatable
+            type TestObject @nonRepeatable
+            extend type TestObject @nonRepeatable
+            """,
+            [
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(4, 36), (5, 29)],
+                },
+                {
+                    "message": "The directive '@nonRepeatable'"
+                    " can only be used once at this location.",
+                    "locations": [(4, 36), (6, 36)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_unique_enum_value_names.py b/tests/validation/test_unique_enum_value_names.py
new file mode 100644
index 0000000..5611b45
--- /dev/null
+++ b/tests/validation/test_unique_enum_value_names.py
@@ -0,0 +1,191 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation.rules.unique_enum_value_names import UniqueEnumValueNamesRule
+
+from .harness import assert_sdl_validation_errors
+
+assert_errors = partial(assert_sdl_validation_errors, UniqueEnumValueNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_field_definition_names():
+    def no_values():
+        assert_valid(
+            """
+            enum SomeEnum
+            """
+        )
+
+    def one_value():
+        assert_valid(
+            """
+            enum SomeEnum {
+              FOO
+            }
+            """
+        )
+
+    def multiple_values():
+        assert_valid(
+            """
+            enum SomeEnum {
+              FOO
+              BAR
+            }
+            """
+        )
+
+    def duplicate_values_inside_the_same_enum_definition():
+        assert_errors(
+            """
+            enum SomeEnum {
+              FOO
+              BAR
+              FOO
+            }
+            """,
+            [
+                {
+                    "message": "Enum value 'SomeEnum.FOO' can only be defined once.",
+                    "locations": [(3, 15), (5, 15)],
+                },
+            ],
+        )
+
+    def extend_enum_with_new_value():
+        assert_valid(
+            """
+            enum SomeEnum {
+              FOO
+            }
+            extend enum SomeEnum {
+              BAR
+            }
+            extend enum SomeEnum {
+              BAZ
+            }
+            """
+        )
+
+    def extend_enum_with_duplicate_value():
+        assert_errors(
+            """
+            extend enum SomeEnum {
+              FOO
+            }
+            enum SomeEnum {
+              FOO
+            }
+            """,
+            [
+                {
+                    "message": "Enum value 'SomeEnum.FOO' can only be defined once.",
+                    "locations": [(3, 15), (6, 15)],
+                },
+            ],
+        )
+
+    def duplicate_value_inside_extension():
+        assert_errors(
+            """
+            enum SomeEnum
+            extend enum SomeEnum {
+              FOO
+              BAR
+              FOO
+            }
+            """,
+            [
+                {
+                    "message": "Enum value 'SomeEnum.FOO' can only be defined once.",
+                    "locations": [(4, 15), (6, 15)],
+                },
+            ],
+        )
+
+    def duplicate_value_inside_different_extension():
+        assert_errors(
+            """
+            enum SomeEnum
+            extend enum SomeEnum {
+              FOO
+            }
+            extend enum SomeEnum {
+              FOO
+            }
+            """,
+            [
+                {
+                    "message": "Enum value 'SomeEnum.FOO' can only be defined once.",
+                    "locations": [(4, 15), (7, 15)],
+                },
+            ],
+        )
+
+    def adding_new_value_to_the_enum_inside_existing_schema():
+        schema = build_schema("enum SomeEnum")
+        sdl = """
+          extend enum SomeEnum {
+              FOO
+          }
+          """
+
+        assert_valid(sdl, schema=schema)
+
+    def adding_conflicting_value_to_existing_schema_twice():
+        schema = build_schema(
+            """
+            enum SomeEnum {
+              FOO
+            }
+            """
+        )
+        sdl = """
+            extend enum SomeEnum {
+              FOO
+            }
+            extend enum SomeEnum {
+              FOO
+            }
+            """
+
+        assert_errors(
+            sdl,
+            [
+                {
+                    "message": "Enum value 'SomeEnum.FOO' already exists in the schema."
+                    " It cannot also be defined in this type extension.",
+                    "locations": [(3, 15)],
+                },
+                {
+                    "message": "Enum value 'SomeEnum.FOO' already exists in the schema."
+                    " It cannot also be defined in this type extension.",
+                    "locations": [(6, 15)],
+                },
+            ],
+            schema,
+        )
+
+    def adding_enum_values_to_existing_schema_twice():
+        schema = build_schema("enum SomeEnum")
+        sdl = """
+            extend enum SomeEnum {
+              FOO
+            }
+            extend enum SomeEnum {
+              FOO
+            }
+            """
+
+        assert_errors(
+            sdl,
+            [
+                {
+                    "message": "Enum value 'SomeEnum.FOO' can only be defined once.",
+                    "locations": [(3, 15), (6, 15)],
+                },
+            ],
+            schema,
+        )
diff --git a/tests/validation/test_unique_field_definition_names.py b/tests/validation/test_unique_field_definition_names.py
new file mode 100644
index 0000000..5e9b1c2
--- /dev/null
+++ b/tests/validation/test_unique_field_definition_names.py
@@ -0,0 +1,407 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation.rules.unique_field_definition_names import (
+    UniqueFieldDefinitionNamesRule,
+)
+
+from .harness import assert_sdl_validation_errors
+
+assert_errors = partial(assert_sdl_validation_errors, UniqueFieldDefinitionNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_field_definition_names():
+    def no_fields():
+        assert_valid(
+            """
+            type SomeObject
+            interface SomeInterface
+            input SomeInputObject
+            """
+        )
+
+    def one_field():
+        assert_valid(
+            """
+            type SomeObject {
+              foo: String
+            }
+
+            interface SomeInterface {
+              foo: String
+            }
+
+            input SomeInputObject {
+              foo: String
+            }
+            """
+        )
+
+    def multiple_fields():
+        assert_valid(
+            """
+            type SomeObject {
+              foo: String
+              bar: String
+            }
+
+            interface SomeInterface {
+              foo: String
+              bar: String
+            }
+
+            input SomeInputObject {
+              foo: String
+              bar: String
+            }
+            """
+        )
+
+    def duplicate_fields_inside_the_same_type_definition():
+        assert_errors(
+            """
+            type SomeObject {
+              foo: String
+              bar: String
+              foo: String
+            }
+
+            interface SomeInterface {
+              foo: String
+              bar: String
+              foo: String
+            }
+
+            input SomeInputObject {
+              foo: String
+              bar: String
+              foo: String
+            }
+            """,
+            [
+                {
+                    "message": "Field 'SomeObject.foo' can only be defined once.",
+                    "locations": [(3, 15), (5, 15)],
+                },
+                {
+                    "message": "Field 'SomeInterface.foo' can only be defined once.",
+                    "locations": [(9, 15), (11, 15)],
+                },
+                {
+                    "message": "Field 'SomeInputObject.foo' can only be defined once.",
+                    "locations": [(15, 15), (17, 15)],
+                },
+            ],
+        )
+
+    def extend_type_with_new_field():
+        assert_valid(
+            """
+            type SomeObject {
+              foo: String
+            }
+            extend type SomeObject {
+              bar: String
+            }
+            extend type SomeObject {
+              baz: String
+            }
+
+            interface SomeInterface {
+              foo: String
+            }
+            extend interface SomeInterface {
+              bar: String
+            }
+            extend interface SomeInterface {
+              baz: String
+            }
+
+            input SomeInputObject {
+              foo: String
+            }
+            extend input SomeInputObject {
+              bar: String
+            }
+            extend input SomeInputObject {
+              baz: String
+            }
+            """
+        )
+
+    def extend_type_with_duplicate_field():
+        assert_errors(
+            """
+            extend type SomeObject {
+              foo: String
+            }
+            type SomeObject {
+              foo: String
+            }
+
+            extend interface SomeInterface {
+              foo: String
+            }
+            interface SomeInterface {
+              foo: String
+            }
+
+            extend input SomeInputObject {
+              foo: String
+            }
+            input SomeInputObject {
+              foo: String
+            }
+            """,
+            [
+                {
+                    "message": "Field 'SomeObject.foo' can only be defined once.",
+                    "locations": [(3, 15), (6, 15)],
+                },
+                {
+                    "message": "Field 'SomeInterface.foo' can only be defined once.",
+                    "locations": [(10, 15), (13, 15)],
+                },
+                {
+                    "message": "Field 'SomeInputObject.foo' can only be defined once.",
+                    "locations": [(17, 15), (20, 15)],
+                },
+            ],
+        )
+
+    def duplicate_field_inside_extension():
+        assert_errors(
+            """
+            type SomeObject
+            extend type SomeObject {
+              foo: String
+              bar: String
+              foo: String
+            }
+
+            interface SomeInterface
+            extend interface SomeInterface {
+              foo: String
+              bar: String
+              foo: String
+            }
+
+            input SomeInputObject
+            extend input SomeInputObject {
+              foo: String
+              bar: String
+              foo: String
+            }
+            """,
+            [
+                {
+                    "message": "Field 'SomeObject.foo' can only be defined once.",
+                    "locations": [(4, 15), (6, 15)],
+                },
+                {
+                    "message": "Field 'SomeInterface.foo' can only be defined once.",
+                    "locations": [(11, 15), (13, 15)],
+                },
+                {
+                    "message": "Field 'SomeInputObject.foo' can only be defined once.",
+                    "locations": [(18, 15), (20, 15)],
+                },
+            ],
+        )
+
+    def duplicate_field_inside_different_extension():
+        assert_errors(
+            """
+            type SomeObject
+            extend type SomeObject {
+              foo: String
+            }
+            extend type SomeObject {
+              foo: String
+            }
+
+            interface SomeInterface
+            extend interface SomeInterface {
+              foo: String
+            }
+            extend interface SomeInterface {
+              foo: String
+            }
+
+            input SomeInputObject
+            extend input SomeInputObject {
+              foo: String
+            }
+            extend input SomeInputObject {
+              foo: String
+            }
+            """,
+            [
+                {
+                    "message": "Field 'SomeObject.foo' can only be defined once.",
+                    "locations": [(4, 15), (7, 15)],
+                },
+                {
+                    "message": "Field 'SomeInterface.foo' can only be defined once.",
+                    "locations": [(12, 15), (15, 15)],
+                },
+                {
+                    "message": "Field 'SomeInputObject.foo' can only be defined once.",
+                    "locations": [(20, 15), (23, 15)],
+                },
+            ],
+        )
+
+    def adding_new_field_to_the_type_inside_existing_schema():
+        schema = build_schema(
+            """
+            type SomeObject
+            interface SomeInterface
+            input SomeInputObject
+            """
+        )
+        sdl = """
+            extend type SomeObject {
+              foo: String
+            }
+
+            extend interface SomeInterface {
+              foo: String
+            }
+
+            extend input SomeInputObject {
+              foo: String
+            }
+            """
+
+        assert_valid(sdl, schema=schema)
+
+    def adding_conflicting_fields_to_existing_schema_twice():
+        schema = build_schema(
+            """
+            type SomeObject {
+              foo: String
+            }
+
+            interface SomeInterface {
+              foo: String
+            }
+
+            input SomeInputObject {
+              foo: String
+            }
+            """
+        )
+        sdl = """
+            extend type SomeObject {
+              foo: String
+            }
+            extend interface SomeInterface {
+              foo: String
+            }
+            extend input SomeInputObject {
+              foo: String
+            }
+
+            extend type SomeObject {
+              foo: String
+            }
+            extend interface SomeInterface {
+              foo: String
+            }
+            extend input SomeInputObject {
+              foo: String
+            }
+            """
+
+        assert_errors(
+            sdl,
+            [
+                {
+                    "message": "Field 'SomeObject.foo' already exists in the schema."
+                    " It cannot also be defined in this type extension.",
+                    "locations": [(3, 15)],
+                },
+                {
+                    "message": "Field 'SomeInterface.foo' already exists in the schema."
+                    " It cannot also be defined in this type extension.",
+                    "locations": [(6, 15)],
+                },
+                {
+                    "message": "Field 'SomeInputObject.foo'"
+                    " already exists in the schema."
+                    " It cannot also be defined in this type extension.",
+                    "locations": [(9, 15)],
+                },
+                {
+                    "message": "Field 'SomeObject.foo' already exists in the schema."
+                    " It cannot also be defined in this type extension.",
+                    "locations": [(13, 15)],
+                },
+                {
+                    "message": "Field 'SomeInterface.foo'"
+                    " already exists in the schema."
+                    " It cannot also be defined in this type extension.",
+                    "locations": [(16, 15)],
+                },
+                {
+                    "message": "Field 'SomeInputObject.foo'"
+                    " already exists in the schema."
+                    " It cannot also be defined in this type extension.",
+                    "locations": [(19, 15)],
+                },
+            ],
+            schema,
+        )
+
+    def adding_fields_to_existing_schema_twice():
+        schema = build_schema(
+            """
+            type SomeObject
+            interface SomeInterface
+            input SomeInputObject
+            """
+        )
+        sdl = """
+            extend type SomeObject {
+              foo: String
+            }
+            extend type SomeObject {
+              foo: String
+            }
+
+            extend interface SomeInterface {
+              foo: String
+            }
+            extend interface SomeInterface {
+              foo: String
+            }
+
+            extend input SomeInputObject {
+              foo: String
+            }
+            extend input SomeInputObject {
+              foo: String
+            }
+            """
+
+        assert_errors(
+            sdl,
+            [
+                {
+                    "message": "Field 'SomeObject.foo' can only be defined once.",
+                    "locations": [(3, 15), (6, 15)],
+                },
+                {
+                    "message": "Field 'SomeInterface.foo' can only be defined once.",
+                    "locations": [(10, 15), (13, 15)],
+                },
+                {
+                    "message": "Field 'SomeInputObject.foo' can only be defined once.",
+                    "locations": [(17, 15), (20, 15)],
+                },
+            ],
+            schema,
+        )
diff --git a/tests/validation/test_unique_fragment_names.py b/tests/validation/test_unique_fragment_names.py
new file mode 100644
index 0000000..37370c9
--- /dev/null
+++ b/tests/validation/test_unique_fragment_names.py
@@ -0,0 +1,117 @@
+from functools import partial
+
+from graphql.validation import UniqueFragmentNamesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, UniqueFragmentNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_fragment_names():
+    def no_fragments():
+        assert_valid(
+            """
+            {
+              field
+            }
+            """
+        )
+
+    def one_fragment():
+        assert_valid(
+            """
+            {
+              ...fragA
+            }
+            fragment fragA on Type {
+              field
+            }
+            """
+        )
+
+    def many_fragments():
+        assert_valid(
+            """
+            {
+              ...fragA
+              ...fragB
+              ...fragC
+            }
+            fragment fragA on Type {
+              fieldA
+            }
+            fragment fragB on Type {
+              fieldB
+            }
+            fragment fragC on Type {
+              fieldC
+            }
+            """
+        )
+
+    def inline_fragments_are_always_unique():
+        assert_valid(
+            """
+            {
+              ...on Type {
+                fieldA
+              }
+              ...on Type {
+                fieldB
+              }
+            }
+            """
+        )
+
+    def fragment_and_operation_named_the_same():
+        assert_valid(
+            """
+            query Foo {
+              ...Foo
+            }
+            fragment Foo on Type {
+              field
+            }
+            """
+        )
+
+    def fragments_named_the_same():
+        assert_errors(
+            """
+            {
+              ...fragA
+            }
+            fragment fragA on Type {
+              fieldA
+            }
+            fragment fragA on Type {
+              fieldB
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one fragment named 'fragA'.",
+                    "locations": [(5, 22), (8, 22)],
+                },
+            ],
+        )
+
+    def fragments_named_the_same_without_being_referenced():
+        assert_errors(
+            """
+            fragment fragA on Type {
+              fieldA
+            }
+            fragment fragA on Type {
+              fieldB
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one fragment named 'fragA'.",
+                    "locations": [(2, 22), (5, 22)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_unique_input_field_names.py b/tests/validation/test_unique_input_field_names.py
new file mode 100644
index 0000000..857118d
--- /dev/null
+++ b/tests/validation/test_unique_input_field_names.py
@@ -0,0 +1,104 @@
+from functools import partial
+
+from graphql.validation import UniqueInputFieldNamesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, UniqueInputFieldNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_input_field_names():
+    def input_object_with_fields():
+        assert_valid(
+            """
+            {
+              field(arg: { f: true })
+            }
+            """
+        )
+
+    def same_input_object_within_two_args():
+        assert_valid(
+            """
+            {
+              field(arg1: { f: true }, arg2: { f: true })
+            }
+            """
+        )
+
+    def multiple_input_object_fields():
+        assert_valid(
+            """
+            {
+              field(arg: { f1: "value", f2: "value", f3: "value" })
+            }
+            """
+        )
+
+    def allows_for_nested_input_objects_with_similar_fields():
+        assert_valid(
+            """
+            {
+              field(arg: {
+                deep: {
+                  deep: {
+                    id: 1
+                  }
+                  id: 1
+                }
+                id: 1
+              })
+            }
+            """
+        )
+
+    def duplicate_input_object_fields():
+        assert_errors(
+            """
+            {
+              field(arg: { f1: "value", f1: "value" })
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one input field named 'f1'.",
+                    "locations": [(3, 28), (3, 41)],
+                },
+            ],
+        )
+
+    def many_duplicate_input_object_fields():
+        assert_errors(
+            """
+            {
+              field(arg: { f1: "value", f1: "value", f1: "value" })
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one input field named 'f1'.",
+                    "locations": [(3, 28), (3, 41)],
+                },
+                {
+                    "message": "There can be only one input field named 'f1'.",
+                    "locations": [(3, 28), (3, 54)],
+                },
+            ],
+        )
+
+    def nested_duplicate_input_object_fields():
+        assert_errors(
+            """
+            {
+              field(arg: { f1: {f2: "value", f2: "value" }})
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one input field named 'f2'.",
+                    "locations": [(3, 33), (3, 46)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_unique_operation_names.py b/tests/validation/test_unique_operation_names.py
new file mode 100644
index 0000000..f2ba8a2
--- /dev/null
+++ b/tests/validation/test_unique_operation_names.py
@@ -0,0 +1,134 @@
+from functools import partial
+
+from graphql.validation import UniqueOperationNamesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, UniqueOperationNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_operation_names():
+    def no_operations():
+        assert_valid(
+            """
+            fragment fragA on Type {
+              field
+            }
+            """
+        )
+
+    def one_anon_operation():
+        assert_valid(
+            """
+            {
+              field
+            }
+            """
+        )
+
+    def one_named_operation():
+        assert_valid(
+            """
+            query Foo {
+              field
+            }
+            """
+        )
+
+    def multiple_operations():
+        assert_valid(
+            """
+            query Foo {
+              field
+            }
+
+            query Bar {
+              field
+            }
+            """
+        )
+
+    def multiple_operations_of_different_types():
+        assert_valid(
+            """
+            query Foo {
+              field
+            }
+
+            mutation Bar {
+              field
+            }
+
+            subscription Baz {
+              field
+            }
+            """
+        )
+
+    def fragment_and_operation_named_the_same():
+        assert_valid(
+            """
+            query Foo {
+              ...Foo
+            }
+            fragment Foo on Type {
+              field
+            }
+            """
+        )
+
+    def multiple_operations_of_same_name():
+        assert_errors(
+            """
+            query Foo {
+              fieldA
+            }
+            query Foo {
+              fieldB
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one operation named 'Foo'.",
+                    "locations": [(2, 19), (5, 19)],
+                },
+            ],
+        )
+
+    def multiple_ops_of_same_name_of_different_types_mutation():
+        assert_errors(
+            """
+            query Foo {
+              fieldA
+            }
+            mutation Foo {
+              fieldB
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one operation named 'Foo'.",
+                    "locations": [(2, 19), (5, 22)],
+                },
+            ],
+        )
+
+    def multiple_ops_of_same_name_of_different_types_subscription():
+        assert_errors(
+            """
+            query Foo {
+              fieldA
+            }
+            subscription Foo {
+              fieldB
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one operation named 'Foo'.",
+                    "locations": [(2, 19), (5, 26)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_unique_operation_types.py b/tests/validation/test_unique_operation_types.py
new file mode 100644
index 0000000..c341e84
--- /dev/null
+++ b/tests/validation/test_unique_operation_types.py
@@ -0,0 +1,352 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation.rules.unique_operation_types import UniqueOperationTypesRule
+
+from .harness import assert_sdl_validation_errors
+
+assert_errors = partial(assert_sdl_validation_errors, UniqueOperationTypesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_operation_types():
+    def no_schema_definition():
+        assert_valid(
+            """
+            type Foo
+            """
+        )
+
+    def schema_definition_with_all_types():
+        assert_valid(
+            """
+            type Foo
+
+            schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+            """
+        )
+
+    def schema_definition_with_single_extension():
+        assert_valid(
+            """
+            type Foo
+
+            schema { query: Foo }
+
+            extend schema {
+              mutation: Foo
+              subscription: Foo
+            }
+            """
+        )
+
+    def schema_definition_with_separate_extensions():
+        assert_valid(
+            """
+            type Foo
+
+            schema { query: Foo }
+            extend schema { mutation: Foo }
+            extend schema { subscription: Foo }
+            """
+        )
+
+    def extend_schema_before_definition():
+        assert_valid(
+            """
+            type Foo
+
+            extend schema { mutation: Foo }
+            extend schema { subscription: Foo }
+
+            schema { query: Foo }
+            """
+        )
+
+    def duplicate_operation_types_inside_single_schema_definition():
+        assert_errors(
+            """
+            type Foo
+
+            schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one query type in schema.",
+                    "locations": [(5, 15), (9, 15)],
+                },
+                {
+                    "message": "There can be only one mutation type in schema.",
+                    "locations": [(6, 15), (10, 15)],
+                },
+                {
+                    "message": "There can be only one subscription type in schema.",
+                    "locations": [(7, 15), (11, 15)],
+                },
+            ],
+        )
+
+    def duplicate_operation_types_inside_schema_extension():
+        assert_errors(
+            """
+            type Foo
+
+            schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+
+            extend schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one query type in schema.",
+                    "locations": [(5, 15), (11, 15)],
+                },
+                {
+                    "message": "There can be only one mutation type in schema.",
+                    "locations": [(6, 15), (12, 15)],
+                },
+                {
+                    "message": "There can be only one subscription type in schema.",
+                    "locations": [(7, 15), (13, 15)],
+                },
+            ],
+        )
+
+    def duplicate_operation_types_inside_schema_extension_twice():
+        assert_errors(
+            """
+            type Foo
+
+            schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+
+            extend schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+
+            extend schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one query type in schema.",
+                    "locations": [(5, 15), (11, 15)],
+                },
+                {
+                    "message": "There can be only one mutation type in schema.",
+                    "locations": [(6, 15), (12, 15)],
+                },
+                {
+                    "message": "There can be only one subscription type in schema.",
+                    "locations": [(7, 15), (13, 15)],
+                },
+                {
+                    "message": "There can be only one query type in schema.",
+                    "locations": [(5, 15), (17, 15)],
+                },
+                {
+                    "message": "There can be only one mutation type in schema.",
+                    "locations": [(6, 15), (18, 15)],
+                },
+                {
+                    "message": "There can be only one subscription type in schema.",
+                    "locations": [(7, 15), (19, 15)],
+                },
+            ],
+        )
+
+    def duplicate_operation_types_inside_second_schema_extension():
+        assert_errors(
+            """
+            type Foo
+
+            schema {
+              query: Foo
+            }
+
+            extend schema {
+              mutation: Foo
+              subscription: Foo
+            }
+
+            extend schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+            """,
+            [
+                {
+                    "message": "There can be only one query type in schema.",
+                    "locations": [(5, 15), (14, 15)],
+                },
+                {
+                    "message": "There can be only one mutation type in schema.",
+                    "locations": [(9, 15), (15, 15)],
+                },
+                {
+                    "message": "There can be only one subscription type in schema.",
+                    "locations": [(10, 15), (16, 15)],
+                },
+            ],
+        )
+
+    def define_schema_inside_extension_sdl():
+        schema = build_schema("type Foo")
+        sdl = """
+            schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+            """
+
+        assert_valid(sdl, schema=schema)
+
+    def define_and_extend_schema_inside_extension_sdl():
+        schema = build_schema("type Foo")
+        sdl = """
+            schema { query: Foo }
+            extend schema { mutation: Foo }
+            extend schema { subscription: Foo }
+            """
+
+        assert_valid(sdl, schema=schema)
+
+    def adding_new_operation_types_to_existing_schema():
+        schema = build_schema("type Query")
+        sdl = """
+            extend schema { mutation: Foo }
+            extend schema { subscription: Foo }
+            """
+
+        assert_valid(sdl, schema=schema)
+
+    def adding_conflicting_operation_types_to_existing_schema():
+        schema = build_schema(
+            """
+            type Query
+            type Mutation
+            type Subscription
+
+            type Foo
+            """
+        )
+
+        sdl = """
+            extend schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+            """
+
+        assert_errors(
+            sdl,
+            [
+                {
+                    "message": "Type for query already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(3, 15)],
+                },
+                {
+                    "message": "Type for mutation already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(4, 15)],
+                },
+                {
+                    "message": "Type for subscription already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(5, 15)],
+                },
+            ],
+            schema,
+        )
+
+    def adding_conflicting_operation_types_to_existing_schema_twice():
+        schema = build_schema(
+            """
+            type Query
+            type Mutation
+            type Subscription
+            """
+        )
+
+        sdl = """
+            extend schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+            }
+
+            extend schema {
+              query: Foo
+              mutation: Foo
+              subscription: Foo
+             }
+            """
+
+        assert_errors(
+            sdl,
+            [
+                {
+                    "message": "Type for query already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(3, 15)],
+                },
+                {
+                    "message": "Type for mutation already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(4, 15)],
+                },
+                {
+                    "message": "Type for subscription already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(5, 15)],
+                },
+                {
+                    "message": "Type for query already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(9, 15)],
+                },
+                {
+                    "message": "Type for mutation already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(10, 15)],
+                },
+                {
+                    "message": "Type for subscription already defined in the schema."
+                    " It cannot be redefined.",
+                    "locations": [(11, 15)],
+                },
+            ],
+            schema,
+        )
diff --git a/tests/validation/test_unique_type_names.py b/tests/validation/test_unique_type_names.py
new file mode 100644
index 0000000..1ff03b8
--- /dev/null
+++ b/tests/validation/test_unique_type_names.py
@@ -0,0 +1,144 @@
+from functools import partial
+
+from graphql.utilities import build_schema
+from graphql.validation.rules.unique_type_names import UniqueTypeNamesRule
+
+from .harness import assert_sdl_validation_errors
+
+assert_errors = partial(assert_sdl_validation_errors, UniqueTypeNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_type_names():
+    def no_types():
+        assert_valid(
+            """
+            directive @test on SCHEMA
+            """
+        )
+
+    def one_type():
+        assert_valid(
+            """
+            type Foo
+            """
+        )
+
+    def many_types():
+        assert_valid(
+            """
+            type Foo
+            type Bar
+            type Baz
+            """
+        )
+
+    def type_and_non_type_definitions_named_the_same():
+        assert_valid(
+            """
+            query Foo { __typename }
+            fragment Foo on Query { __typename }
+            directive @Foo on SCHEMA
+
+            type Foo
+            """
+        )
+
+    def types_named_the_same():
+        assert_errors(
+            """
+            type Foo
+
+            scalar Foo
+            type Foo
+            interface Foo
+            union Foo
+            enum Foo
+            input Foo
+            """,
+            [
+                {
+                    "message": "There can be only one type named 'Foo'.",
+                    "locations": [(2, 18), (4, 20)],
+                },
+                {
+                    "message": "There can be only one type named 'Foo'.",
+                    "locations": [(2, 18), (5, 18)],
+                },
+                {
+                    "message": "There can be only one type named 'Foo'.",
+                    "locations": [(2, 18), (6, 23)],
+                },
+                {
+                    "message": "There can be only one type named 'Foo'.",
+                    "locations": [(2, 18), (7, 19)],
+                },
+                {
+                    "message": "There can be only one type named 'Foo'.",
+                    "locations": [(2, 18), (8, 18)],
+                },
+                {
+                    "message": "There can be only one type named 'Foo'.",
+                    "locations": [(2, 18), (9, 19)],
+                },
+            ],
+        )
+
+    def adding_new_type_to_existing_schema():
+        schema = build_schema("type Foo")
+
+        assert_valid("type Bar", schema=schema)
+
+    def adding_new_type_to_existing_schema_with_same_named_directive():
+        schema = build_schema("directive @Foo on SCHEMA")
+
+        assert_valid("type Foo", schema=schema)
+
+    def adding_conflicting_types_to_existing_schema():
+        schema = build_schema("type Foo")
+        sdl = """
+            scalar Foo
+            type Foo
+            interface Foo
+            union Foo
+            enum Foo
+            input Foo
+            """
+
+        assert_errors(
+            sdl,
+            [
+                {
+                    "message": "Type 'Foo' already exists in the schema."
+                    " It cannot also be defined in this type definition.",
+                    "locations": [(2, 20)],
+                },
+                {
+                    "message": "Type 'Foo' already exists in the schema."
+                    " It cannot also be defined in this type definition.",
+                    "locations": [(3, 18)],
+                },
+                {
+                    "message": "Type 'Foo' already exists in the schema."
+                    " It cannot also be defined in this type definition.",
+                    "locations": [(4, 23)],
+                },
+                {
+                    "message": "Type 'Foo' already exists in the schema."
+                    " It cannot also be defined in this type definition.",
+                    "locations": [(5, 19)],
+                },
+                {
+                    "message": "Type 'Foo' already exists in the schema."
+                    " It cannot also be defined in this type definition.",
+                    "locations": [(6, 18)],
+                },
+                {
+                    "message": "Type 'Foo' already exists in the schema."
+                    " It cannot also be defined in this type definition.",
+                    "locations": [(7, 19)],
+                },
+            ],
+            schema,
+        )
diff --git a/tests/validation/test_unique_variable_names.py b/tests/validation/test_unique_variable_names.py
new file mode 100644
index 0000000..788574e
--- /dev/null
+++ b/tests/validation/test_unique_variable_names.py
@@ -0,0 +1,46 @@
+from functools import partial
+
+from graphql.validation import UniqueVariableNamesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, UniqueVariableNamesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_unique_variable_names():
+    def unique_variable_names():
+        assert_valid(
+            """
+            query A($x: Int, $y: String) { __typename }
+            query B($x: String, $y: Int) { __typename }
+            """
+        )
+
+    def duplicate_variable_names():
+        assert_errors(
+            """
+            query A($x: Int, $x: Int, $x: String) { __typename }
+            query B($x: String, $x: Int) { __typename }
+            query C($x: Int, $x: Int) { __typename }
+            """,
+            [
+                {
+                    "message": "There can be only one variable named '$x'.",
+                    "locations": [(2, 22), (2, 31)],
+                },
+                {
+                    "message": "There can be only one variable named '$x'.",
+                    "locations": [(2, 22), (2, 40)],
+                },
+                {
+                    "message": "There can be only one variable named '$x'.",
+                    "locations": [(3, 22), (3, 34)],
+                },
+                {
+                    "message": "There can be only one variable named '$x'.",
+                    "locations": [(4, 22), (4, 31)],
+                },
+            ],
+        )
diff --git a/tests/validation/test_validation.py b/tests/validation/test_validation.py
new file mode 100644
index 0000000..bf06cb8
--- /dev/null
+++ b/tests/validation/test_validation.py
@@ -0,0 +1,186 @@
+from pytest import raises  # type: ignore
+
+from graphql.error import GraphQLError
+from graphql.language import parse
+from graphql.utilities import TypeInfo, build_schema
+from graphql.validation import ValidationRule, validate
+
+from .harness import test_schema
+
+
+def describe_validate_supports_full_validation():
+    def rejects_invalid_documents():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            assert validate(test_schema, None)  # type: ignore
+        assert str(exc_info.value) == "Must provide document."
+
+    def rejects_invalid_type_info():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            assert validate(
+                test_schema, parse("query { name }"), type_info={}  # type: ignore
+            )
+        assert str(exc_info.value) == "Not a TypeInfo object: {}."
+
+    def rejects_invalid_rules():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            assert validate(
+                test_schema, parse("query { name }"), rules=[None]  # type: ignore
+            )
+        assert (
+            str(exc_info.value) == "Rules must be specified as a collection"
+            " of ASTValidationRule subclasses."
+        )
+
+    def rejects_invalid_max_errors():
+        with raises(TypeError) as exc_info:
+            # noinspection PyTypeChecker
+            assert validate(
+                test_schema, parse("query { name }"), max_errors=2.5  # type: ignore
+            )
+        assert (
+            str(exc_info.value)
+            == "The maximum number of errors must be passed as an int."
+        )
+
+    def validates_queries():
+        doc = parse(
+            """
+            query {
+              catOrDog {
+                ... on Cat {
+                  furColor
+                }
+                ... on Dog {
+                  isHouseTrained
+                }
+              }
+            }
+            """
+        )
+
+        errors = validate(test_schema, doc)
+        assert errors == []
+
+    def detects_unknown_fields():
+        doc = parse(
+            """
+            {
+              unknown
+            }
+            """
+        )
+
+        errors = validate(test_schema, doc)
+        assert errors == [
+            {"message": "Cannot query field 'unknown' on type 'QueryRoot'."}
+        ]
+
+    # NOTE: experimental
+    def validates_using_a_custom_type_info():
+        # This TypeInfo will never return a valid field.
+        type_info = TypeInfo(test_schema, lambda *args: None)
+
+        doc = parse(
+            """
+            query {
+              catOrDog {
+                ... on Cat {
+                  furColor
+                }
+                ... on Dog {
+                  isHouseTrained
+                }
+              }
+            }
+            """
+        )
+
+        errors = validate(test_schema, doc, None, type_info)
+
+        assert [error.message for error in errors] == [
+            "Cannot query field 'catOrDog' on type 'QueryRoot'."
+            " Did you mean 'catOrDog'?",
+            "Cannot query field 'furColor' on type 'Cat'. Did you mean 'furColor'?",
+            "Cannot query field 'isHouseTrained' on type 'Dog'."
+            " Did you mean 'isHouseTrained'?",
+        ]
+
+    def validates_using_a_custom_rule():
+        schema = build_schema(
+            """
+            directive @custom(arg: String) on FIELD
+
+            type Query {
+              foo: String
+            }
+            """
+        )
+
+        doc = parse(
+            """
+            query {
+              name @custom
+            }
+            """
+        )
+
+        class CustomRule(ValidationRule):
+            def enter_directive(self, node, *_args):
+                directive_def = self.context.get_directive()
+                error = GraphQLError("Reporting directive: " + str(directive_def), node)
+                self.context.report_error(error)
+
+        errors = validate(schema, doc, [CustomRule])
+        assert errors == [
+            {"message": "Reporting directive: @custom", "locations": [(3, 20)]}
+        ]
+
+
+def describe_validate_limit_maximum_number_of_validation_errors():
+    query = """
+        {
+          firstUnknownField
+          secondUnknownField
+          thirdUnknownField
+        }
+        """
+    doc = parse(query, no_location=True)
+
+    def _validate_document(max_errors=None):
+        return validate(test_schema, doc, max_errors=max_errors)
+
+    def _invalid_field_error(field_name: str):
+        return {
+            "message": f"Cannot query field '{field_name}' on type 'QueryRoot'.",
+            "locations": [],
+        }
+
+    def when_max_errors_is_equal_to_number_of_errors():
+        errors = _validate_document(max_errors=3)
+        assert errors == [
+            _invalid_field_error("firstUnknownField"),
+            _invalid_field_error("secondUnknownField"),
+            _invalid_field_error("thirdUnknownField"),
+        ]
+
+    def when_max_errors_is_less_than_number_of_errors():
+        errors = _validate_document(max_errors=2)
+        assert errors == [
+            _invalid_field_error("firstUnknownField"),
+            _invalid_field_error("secondUnknownField"),
+            {
+                "message": "Too many validation errors, error limit reached."
+                " Validation aborted."
+            },
+        ]
+
+    def pass_through_exceptions_from_rules():
+        class CustomRule(ValidationRule):
+            def enter_field(self, *_args):
+                raise RuntimeError("Error from custom rule!")
+
+        with raises(RuntimeError, match="^Error from custom rule!$"):
+            validate(test_schema, doc, [CustomRule], max_errors=1)
diff --git a/tests/validation/test_values_of_correct_type.py b/tests/validation/test_values_of_correct_type.py
new file mode 100644
index 0000000..a41cd9f
--- /dev/null
+++ b/tests/validation/test_values_of_correct_type.py
@@ -0,0 +1,1270 @@
+from functools import partial
+
+from graphql.pyutils import Undefined, inspect
+from graphql.type import (
+    GraphQLArgument,
+    GraphQLField,
+    GraphQLObjectType,
+    GraphQLSchema,
+    GraphQLScalarType,
+    GraphQLString,
+)
+from graphql.validation import ValuesOfCorrectTypeRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, ValuesOfCorrectTypeRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_values_of_correct_type():
+    def describe_valid_values():
+        def good_int_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    intArgField(intArg: 2)
+                  }
+                }
+                """
+            )
+
+        def good_negative_int_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    intArgField(intArg: -2)
+                  }
+                }
+                """
+            )
+
+        def good_boolean_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    booleanArgField(intArg: true)
+                  }
+                }
+                """
+            )
+
+        def good_string_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    stringArgField(intArg: "foo")
+                  }
+                }
+                """
+            )
+
+        def good_float_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    floatArgField(intArg: 1.1)
+                  }
+                }
+                """
+            )
+
+        def good_negative_float_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    floatArgField(intArg: -1.1)
+                  }
+                }
+                """
+            )
+
+        def int_into_id():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    idArgField(idArg: 1)
+                  }
+                }
+                """
+            )
+
+        def string_into_id():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    idArgField(idArg: "someIdString")
+                  }
+                }
+                """
+            )
+
+        def good_enum_value():
+            assert_valid(
+                """
+                {
+                  dog {
+                    doesKnowCommand(dogCommand: SIT)
+                  }
+                }
+                """
+            )
+
+        def enum_with_undefined_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    enumArgField(enumArg: UNKNOWN)
+                  }
+                }
+                """
+            )
+
+        def enum_with_null_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    enumArgField(enumArg: NO_FUR)
+                  }
+                }
+                """
+            )
+
+        def null_into_nullable_type():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    intArgField(intArg: null)
+                  }
+                }
+                """
+            )
+
+            assert_valid(
+                """
+                {
+                  dog(a: null, b: null, c:{ requiredField: true, intField: null }) {
+                    name
+                  }
+                }
+                """
+            )
+
+    def describe_invalid_string_values():
+        def int_into_string():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    stringArgField(stringArg: 1)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "String cannot represent a non string value: 1",
+                        "locations": [(4, 47)],
+                    },
+                ],
+            )
+
+        def float_into_string():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    stringArgField(stringArg: 1.0)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "String cannot represent a non string value: 1.0",
+                        "locations": [(4, 47)],
+                    },
+                ],
+            )
+
+        def boolean_into_string():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    stringArgField(stringArg: true)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "String cannot represent a non string value: true",
+                        "locations": [(4, 47)],
+                    },
+                ],
+            )
+
+        def unquoted_string_into_string():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    stringArgField(stringArg: BAR)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "String cannot represent a non string value: BAR",
+                        "locations": [(4, 47)],
+                    },
+                ],
+            )
+
+    def describe_invalid_int_values():
+        def string_into_int():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    intArgField(intArg: "3")
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": 'Int cannot represent non-integer value: "3"',
+                        "locations": [(4, 41)],
+                    },
+                ],
+            )
+
+        def big_int_into_int():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    intArgField(intArg: 829384293849283498239482938)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Int cannot represent non 32-bit signed integer"
+                        " value: 829384293849283498239482938",
+                        "locations": [(4, 41)],
+                    },
+                ],
+            )
+
+        def unquoted_string_into_int():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    intArgField(intArg: FOO)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Int cannot represent non-integer value: FOO",
+                        "locations": [(4, 41)],
+                    },
+                ],
+            )
+
+        def simple_float_into_int():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    intArgField(intArg: 3.0)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Int cannot represent non-integer value: 3.0",
+                        "locations": [(4, 41)],
+                    }
+                ],
+            )
+
+        def float_into_int():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    intArgField(intArg: 3.333)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Int cannot represent non-integer value: 3.333",
+                        "locations": [(4, 41)],
+                    },
+                ],
+            )
+
+    def describe_invalid_float_values():
+        def string_into_float():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    floatArgField(floatArg: "3.333")
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": 'Float cannot represent non numeric value: "3.333"',
+                        "locations": [(4, 45)],
+                    },
+                ],
+            )
+
+        def boolean_into_float():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    floatArgField(floatArg: true)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Float cannot represent non numeric value: true",
+                        "locations": [(4, 45)],
+                    },
+                ],
+            )
+
+        def unquoted_into_float():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    floatArgField(floatArg: FOO)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Float cannot represent non numeric value: FOO",
+                        "locations": [(4, 45)],
+                    },
+                ],
+            )
+
+    def describe_invalid_boolean_value():
+        def int_into_boolean():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    booleanArgField(booleanArg: 2)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Boolean cannot represent a non boolean value: 2",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+        def float_into_boolean():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    booleanArgField(booleanArg: 1.0)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Boolean cannot represent a non boolean value: 1.0",
+                        "locations": [(4, 49)],
+                    }
+                ],
+            )
+
+        def string_into_boolean():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    booleanArgField(booleanArg: "true")
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Boolean cannot represent a non boolean value:"
+                        ' "true"',
+                        "locations": [(4, 49)],
+                    }
+                ],
+            )
+
+        def unquoted_into_boolean():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    booleanArgField(booleanArg: TRUE)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Boolean cannot represent a non boolean value: TRUE",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+    def describe_invalid_id_value():
+        def float_into_id():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    idArgField(idArg: 1.0)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "ID cannot represent a non-string"
+                        " and non-integer value: 1.0",
+                        "locations": [(4, 39)],
+                    }
+                ],
+            )
+
+        def boolean_into_id():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    idArgField(idArg: true)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "ID cannot represent a non-string"
+                        " and non-integer value: true",
+                        "locations": [(4, 39)],
+                    },
+                ],
+            )
+
+        def unquoted_into_id():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    idArgField(idArg: SOMETHING)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "ID cannot represent a non-string"
+                        " and non-integer value: SOMETHING",
+                        "locations": [(4, 39)],
+                    },
+                ],
+            )
+
+    def describe_invalid_enum_value():
+        def int_into_enum():
+            assert_errors(
+                """
+                {
+                  dog {
+                    doesKnowCommand(dogCommand: 2)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Enum 'DogCommand' cannot represent non-enum value:"
+                        " 2.",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+        def float_into_enum():
+            assert_errors(
+                """
+                {
+                  dog {
+                    doesKnowCommand(dogCommand: 1.0)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Enum 'DogCommand' cannot represent non-enum value:"
+                        " 1.0.",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+        def string_into_enum():
+            assert_errors(
+                """
+                {
+                  dog {
+                    doesKnowCommand(dogCommand: "SIT")
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Enum 'DogCommand' cannot represent non-enum value:"
+                        ' "SIT".'
+                        " Did you mean the enum value 'SIT'?",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+        def boolean_into_enum():
+            assert_errors(
+                """
+                {
+                  dog {
+                    doesKnowCommand(dogCommand: true)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Enum 'DogCommand' cannot represent non-enum value:"
+                        " true.",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+        def unknown_enum_value_into_enum():
+            assert_errors(
+                """
+                {
+                  dog {
+                    doesKnowCommand(dogCommand: JUGGLE)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Value 'JUGGLE'"
+                        " does not exist in 'DogCommand' enum.",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+        def different_case_enum_value_into_enum():
+            assert_errors(
+                """
+                {
+                  dog {
+                    doesKnowCommand(dogCommand: sit)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Value 'sit' does not exist in 'DogCommand' enum."
+                        " Did you mean the enum value 'SIT'?",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+    def describe_valid_list_value():
+        def good_list_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    stringListArgField(stringListArg: ["one", null, "two"])
+                  }
+                }
+                """
+            )
+
+        def empty_list_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    stringListArgField(stringListArg: [])
+                  }
+                }
+                """
+            )
+
+        def null_value():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    stringListArgField(stringListArg: null)
+                  }
+                }
+                """
+            )
+
+        def single_value_into_list():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    stringListArgField(stringListArg: "one")
+                  }
+                }
+                """
+            )
+
+    def describe_invalid_list_value():
+        def incorrect_item_type():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    stringListArgField(stringListArg: ["one", 2])
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "String cannot represent a non string value: 2",
+                        "locations": [(4, 63)],
+                    },
+                ],
+            )
+
+        def single_value_of_incorrect_type():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    stringListArgField(stringListArg: 1)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "String cannot represent a non string value: 1",
+                        "locations": [(4, 55)],
+                    },
+                ],
+            )
+
+    def describe_valid_non_nullable_value():
+        def arg_on_optional_arg():
+            assert_valid(
+                """
+                {
+                  dog {
+                    isHouseTrained(atOtherHomes: true)
+                  }
+                }
+                """
+            )
+
+        def no_arg_on_optional_arg():
+            assert_valid(
+                """
+                {
+                  dog {
+                    isHouseTrained
+                  }
+                }
+                """
+            )
+
+        def multiple_args():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req1: 1, req2: 2)
+                  }
+                }
+                """
+            )
+
+        def multiple_args_reverse_order():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req2: 2, req1: 1)
+                  }
+                }
+                """
+            )
+
+        def no_args_on_multiple_optional():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOpts
+                  }
+                }
+                """
+            )
+
+        def one_arg_on_multiple_optional():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOpts(opt1: 1)
+                  }
+                }
+                """
+            )
+
+        def second_arg_on_multiple_optional():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOpts(opt2: 1)
+                  }
+                }
+                """
+            )
+
+        def multiple_required_args_on_mixed_list():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOptAndReq(req1: 3, req2: 4)
+                  }
+                }
+                """
+            )
+
+        def multiple_required_and_one_optional_arg_on_mixed_list():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOptAndReq(req1: 3, req2: 4, opt1: 5)
+                  }
+                }
+                """
+            )
+
+        def all_required_and_optional_args_on_mixed_list():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    multipleOptAndReq(req1: 3, req2: 4, opt1: 5, opt2: 6)
+                  }
+                }
+                """
+            )
+
+    def describe_invalid_non_nullable_value():
+        def incorrect_value_type():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req2: "two", req1: "one")
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": 'Int cannot represent non-integer value: "two"',
+                        "locations": [(4, 40)],
+                    },
+                    {
+                        "message": 'Int cannot represent non-integer value: "one"',
+                        "locations": [(4, 53)],
+                    },
+                ],
+            )
+
+        def incorrect_value_and_missing_argument_provided_required_arguments():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req1: "one")
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": 'Int cannot represent non-integer value: "one"',
+                        "locations": [(4, 40)],
+                    },
+                ],
+            )
+
+        def null_value():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    multipleReqs(req1: null)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Expected value of type 'Int!', found null.",
+                        "locations": [(4, 40)],
+                    },
+                ],
+            )
+
+    def describe_valid_input_object_value():
+        def optional_arg_despite_required_field_in_type():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField
+                  }
+                }
+                """
+            )
+
+        def partial_object_only_required():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: { requiredField: true })
+                  }
+                }
+                """
+            )
+
+        def partial_object_required_field_can_be_falsy():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: { requiredField: false })
+                  }
+                }
+                """
+            )
+
+        def partial_object_including_required():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: { requiredField: true, intField: 4 })
+                  }
+                }
+                """
+            )
+
+        def full_object():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: {
+                      requiredField: true,
+                      intField: 4,
+                      stringField: "foo",
+                      booleanField: false,
+                      stringListField: ["one", "two"]
+                    })
+                  }
+                }
+                """
+            )
+
+        def full_object_with_fields_in_different_order():
+            assert_valid(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: {
+                      stringListField: ["one", "two"],
+                      booleanField: false,
+                      requiredField: true,
+                      stringField: "foo",
+                      intField: 4,
+                    })
+                  }
+                }
+                """
+            )
+
+    def describe_invalid_input_object_value():
+        def partial_object_missing_required():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: { intField: 4 })
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Field 'ComplexInput.requiredField'"
+                        " of required type 'Boolean!' was not provided.",
+                        "locations": [(4, 49)],
+                    },
+                ],
+            )
+
+        def partial_object_invalid_field_type():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: {
+                      stringListField: ["one", 2],
+                      requiredField: true,
+                    })
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "String cannot represent a non string value: 2",
+                        "locations": [(5, 48)],
+                    },
+                ],
+            )
+
+        def partial_object_null_to_non_null_field():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: {
+                      requiredField: true,
+                      nonNullField: null,
+                    })
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Expected value of type 'Boolean!', found null.",
+                        "locations": [(6, 37)],
+                    }
+                ],
+            )
+
+        def partial_object_unknown_field_arg():
+            assert_errors(
+                """
+                {
+                  complicatedArgs {
+                    complexArgField(complexArg: {
+                      requiredField: true,
+                      invalidField: "value"
+                    })
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Field 'invalidField'"
+                        " is not defined by type 'ComplexInput'."
+                        " Did you mean 'intField'?",
+                        "locations": [(6, 23)],
+                    },
+                ],
+            )
+
+        def reports_original_error_for_custom_scalar_which_throws():
+            def parse_value(value):
+                raise Exception(f"Invalid scalar is always invalid: {inspect(value)}")
+
+            custom_scalar = GraphQLScalarType("Invalid", parse_value=parse_value)
+
+            schema = GraphQLSchema(
+                query=GraphQLObjectType(
+                    "Query",
+                    {
+                        "invalidArg": GraphQLField(
+                            GraphQLString, {"arg": GraphQLArgument(custom_scalar)}
+                        )
+                    },
+                )
+            )
+
+            errors = assert_errors(
+                "{ invalidArg(arg: 123) }",
+                [
+                    {
+                        "message": "Expected value of type 'Invalid', found 123;"
+                        " Invalid scalar is always invalid: 123",
+                        "locations": [(1, 19)],
+                    }
+                ],
+                schema=schema,
+            )
+
+            assert str(errors[0].original_error) == (
+                "Invalid scalar is always invalid: 123"
+            )
+
+        def reports_error_for_custom_scalar_that_returns_undefined():
+            custom_scalar = GraphQLScalarType(
+                "CustomScalar", parse_value=lambda value: Undefined
+            )
+
+            schema = GraphQLSchema(
+                GraphQLObjectType(
+                    "Query",
+                    {
+                        "invalidArg": GraphQLField(
+                            GraphQLString, args={"arg": GraphQLArgument(custom_scalar)}
+                        )
+                    },
+                )
+            )
+
+            assert_errors(
+                "{ invalidArg(arg: 123) }",
+                [
+                    {
+                        "message": "Expected value of type 'CustomScalar', found 123.",
+                        "locations": [(1, 19)],
+                    },
+                ],
+                schema=schema,
+            )
+
+        def allows_custom_scalar_to_accept_complex_literals():
+            custom_scalar = GraphQLScalarType("Any")
+            schema = GraphQLSchema(
+                query=GraphQLObjectType(
+                    "Query",
+                    {
+                        "anyArg": GraphQLField(
+                            GraphQLString, {"arg": GraphQLArgument(custom_scalar)}
+                        )
+                    },
+                )
+            )
+
+            assert_valid(
+                """
+                {
+                  test1: anyArg(arg: 123)
+                  test2: anyArg(arg: "abc")
+                  test3: anyArg(arg: [123, "abc"])
+                  test4: anyArg(arg: {deep: [123, "abc"]})
+                }
+                """,
+                schema=schema,
+            )
+
+    def describe_directive_arguments():
+        def with_directives_of_valid_types():
+            assert_valid(
+                """
+                {
+                  dog @include(if: true) {
+                    name
+                  }
+                  human @skip(if: false) {
+                    name
+                  }
+                }
+                """
+            )
+
+        def with_directives_with_incorrect_types():
+            assert_errors(
+                """
+                {
+                  dog @include(if: "yes") {
+                    name @skip(if: ENUM)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Boolean cannot represent a non boolean value:"
+                        ' "yes"',
+                        "locations": [(3, 36)],
+                    },
+                    {
+                        "message": "Boolean cannot represent a non boolean value: ENUM",
+                        "locations": [(4, 36)],
+                    },
+                ],
+            )
+
+    def describe_variable_default_values():
+        def variables_with_valid_default_values():
+            assert_valid(
+                """
+                query WithDefaultValues(
+                  $a: Int = 1,
+                  $b: String = "ok",
+                  $c: ComplexInput = { requiredField: true, intField: 3 }
+                  $d: Int! = 123
+                ) {
+                  dog { name }
+                }
+                """
+            )
+
+        def variables_with_valid_default_null_values():
+            assert_valid(
+                """
+                query WithDefaultValues(
+                  $a: Int = null,
+                  $b: String = null,
+                  $c: ComplexInput = { requiredField: true, intField: null }
+                ) {
+                  dog { name }
+                }
+                """
+            )
+
+        def variables_with_invalid_default_null_values():
+            assert_errors(
+                """
+                query WithDefaultValues(
+                  $a: Int! = null,
+                  $b: String! = null,
+                  $c: ComplexInput = { requiredField: null, intField: null }
+                ) {
+                  dog { name }
+                }
+                """,
+                [
+                    {
+                        "message": "Expected value of type 'Int!', found null.",
+                        "locations": [(3, 30)],
+                    },
+                    {
+                        "message": "Expected value of type 'String!', found null.",
+                        "locations": [(4, 33)],
+                    },
+                    {
+                        "message": "Expected value of type 'Boolean!', found null.",
+                        "locations": [(5, 55)],
+                    },
+                ],
+            )
+
+        def variables_with_invalid_default_values():
+            assert_errors(
+                """
+                query InvalidDefaultValues(
+                  $a: Int = "one",
+                  $b: String = 4,
+                  $c: ComplexInput = "NotVeryComplex"
+                ) {
+                  dog { name }
+                }
+                """,
+                [
+                    {
+                        "message": 'Int cannot represent non-integer value: "one"',
+                        "locations": [(3, 29)],
+                    },
+                    {
+                        "message": "String cannot represent a non string value: 4",
+                        "locations": [(4, 32)],
+                    },
+                    {
+                        "message": "Expected value of type 'ComplexInput',"
+                        ' found "NotVeryComplex".',
+                        "locations": [(5, 38)],
+                    },
+                ],
+            )
+
+        def variables_with_complex_invalid_default_values():
+            assert_errors(
+                """
+                query WithDefaultValues(
+                  $a: ComplexInput = { requiredField: 123, intField: "abc" }
+                ) {
+                  dog { name }
+                }
+                """,
+                [
+                    {
+                        "message": "Boolean cannot represent a non boolean value: 123",
+                        "locations": [(3, 55)],
+                    },
+                    {
+                        "message": 'Int cannot represent non-integer value: "abc"',
+                        "locations": [(3, 70)],
+                    },
+                ],
+            )
+
+        def complex_variables_missing_required_fields():
+            assert_errors(
+                """
+                query MissingRequiredField($a: ComplexInput = {intField: 3}) {
+                  dog { name }
+                }
+                """,
+                [
+                    {
+                        "message": "Field 'ComplexInput.requiredField'"
+                        " of required type 'Boolean!' was not provided.",
+                        "locations": [(2, 63)],
+                    },
+                ],
+            )
+
+        def list_variables_with_invalid_item():
+            assert_errors(
+                """
+                query InvalidItem($a: [String] = ["one", 2]) {
+                  dog { name }
+                }
+                """,
+                [
+                    {
+                        "message": "String cannot represent a non string value: 2",
+                        "locations": [(2, 58)],
+                    },
+                ],
+            )
diff --git a/tests/validation/test_variables_are_input_types.py b/tests/validation/test_variables_are_input_types.py
new file mode 100644
index 0000000..10e231e
--- /dev/null
+++ b/tests/validation/test_variables_are_input_types.py
@@ -0,0 +1,44 @@
+from functools import partial
+
+from graphql.validation import VariablesAreInputTypesRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, VariablesAreInputTypesRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_variables_are_input_types():
+    def input_types_are_valid():
+        assert_valid(
+            """
+            query Foo($a: String, $b: [Boolean!]!, $c: ComplexInput) {
+              field(a: $a, b: $b, c: $c)
+            }
+            """
+        )
+
+    def output_types_are_invalid():
+        assert_errors(
+            """
+            query Foo($a: Dog, $b: [[CatOrDog!]]!, $c: Pet) {
+              field(a: $a, b: $b, c: $c)
+            }
+            """,
+            [
+                {
+                    "locations": [(2, 27)],
+                    "message": "Variable '$a' cannot be non-input type 'Dog'.",
+                },
+                {
+                    "locations": [(2, 36)],
+                    "message": "Variable '$b' cannot be"
+                    " non-input type '[[CatOrDog!]]!'.",
+                },
+                {
+                    "locations": [(2, 56)],
+                    "message": "Variable '$c' cannot be non-input type 'Pet'.",
+                },
+            ],
+        )
diff --git a/tests/validation/test_variables_in_allowed_position.py b/tests/validation/test_variables_in_allowed_position.py
new file mode 100644
index 0000000..03fbbcb
--- /dev/null
+++ b/tests/validation/test_variables_in_allowed_position.py
@@ -0,0 +1,365 @@
+from functools import partial
+
+from graphql.validation import VariablesInAllowedPositionRule
+
+from .harness import assert_validation_errors
+
+assert_errors = partial(assert_validation_errors, VariablesInAllowedPositionRule)
+
+assert_valid = partial(assert_errors, errors=[])
+
+
+def describe_validate_variables_are_in_allowed_positions():
+    def boolean_to_boolean():
+        assert_valid(
+            """
+            query Query($booleanArg: Boolean)
+            {
+              complicatedArgs {
+                booleanArgField(booleanArg: $booleanArg)
+              }
+            }
+            """
+        )
+
+    def boolean_to_boolean_in_fragment():
+        assert_valid(
+            """
+            fragment booleanArgFrag on ComplicatedArgs {
+              booleanArgField(booleanArg: $booleanArg)
+            }
+            query Query($booleanArg: Boolean)
+            {
+              complicatedArgs {
+                ...booleanArgFrag
+              }
+            }
+            """
+        )
+
+        assert_valid(
+            """
+            query Query($booleanArg: Boolean)
+            {
+              complicatedArgs {
+                ...booleanArgFrag
+              }
+            }
+            fragment booleanArgFrag on ComplicatedArgs {
+              booleanArgField(booleanArg: $booleanArg)
+            }
+            """
+        )
+
+    def non_null_boolean_to_boolean():
+        assert_valid(
+            """
+            query Query($nonNullBooleanArg: Boolean!)
+            {
+              complicatedArgs {
+                booleanArgField(booleanArg: $nonNullBooleanArg)
+              }
+            }
+            """
+        )
+
+    def non_null_boolean_to_boolean_within_fragment():
+        assert_valid(
+            """
+            fragment booleanArgFrag on ComplicatedArgs {
+              booleanArgField(booleanArg: $nonNullBooleanArg)
+            }
+
+            query Query($nonNullBooleanArg: Boolean!)
+            {
+              complicatedArgs {
+                ...booleanArgFrag
+              }
+            }
+            """
+        )
+
+    def array_of_string_to_array_of_string():
+        assert_valid(
+            """
+            query Query($stringListVar: [String])
+            {
+              complicatedArgs {
+                stringListArgField(stringListArg: $stringListVar)
+              }
+            }
+            """
+        )
+
+    def array_of_non_null_string_to_array_of_string():
+        assert_valid(
+            """
+            query Query($stringListVar: [String!])
+            {
+              complicatedArgs {
+                stringListArgField(stringListArg: $stringListVar)
+              }
+            }
+            """
+        )
+
+    def string_to_array_of_string_in_item_position():
+        assert_valid(
+            """
+            query Query($stringVar: String)
+            {
+              complicatedArgs {
+                stringListArgField(stringListArg: [$stringVar])
+              }
+            }
+            """
+        )
+
+    def non_null_string_to_array_of_string_in_item_position():
+        assert_valid(
+            """
+            query Query($stringVar: String!)
+            {
+              complicatedArgs {
+                stringListArgField(stringListArg: [$stringVar])
+              }
+            }
+            """
+        )
+
+    def complex_input_to_complex_input():
+        assert_valid(
+            """
+            query Query($complexVar: ComplexInput)
+            {
+              complicatedArgs {
+                complexArgField(complexArg: $complexVar)
+              }
+            }
+            """
+        )
+
+    def complex_input_to_complex_input_in_field_position():
+        assert_valid(
+            """
+            query Query($boolVar: Boolean = false)
+            {
+              complicatedArgs {
+                complexArgField(complexArg: {requiredArg: $boolVar})
+              }
+            }
+            """
+        )
+
+    def non_null_boolean_to_non_null_boolean_in_directive():
+        assert_valid(
+            """
+            query Query($boolVar: Boolean!)
+            {
+              dog @include(if: $boolVar)
+            }
+            """
+        )
+
+    def int_to_non_null_int():
+        assert_errors(
+            """
+            query Query($intArg: Int) {
+              complicatedArgs {
+                nonNullIntArgField(nonNullIntArg: $intArg)
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$intArg' of type 'Int'"
+                    " used in position expecting type 'Int!'.",
+                    "locations": [(2, 25), (4, 51)],
+                }
+            ],
+        )
+
+    def int_to_non_null_int_within_fragment():
+        assert_errors(
+            """
+            fragment nonNullIntArgFieldFrag on ComplicatedArgs {
+              nonNullIntArgField(nonNullIntArg: $intArg)
+            }
+
+            query Query($intArg: Int) {
+              complicatedArgs {
+                ...nonNullIntArgFieldFrag
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$intArg' of type 'Int'"
+                    " used in position expecting type 'Int!'.",
+                    "locations": [(6, 25), (3, 49)],
+                }
+            ],
+        )
+
+    def int_to_non_null_int_within_nested_fragment():
+        assert_errors(
+            """
+            fragment outerFrag on ComplicatedArgs {
+              ...nonNullIntArgFieldFrag
+            }
+
+            fragment nonNullIntArgFieldFrag on ComplicatedArgs {
+              nonNullIntArgField(nonNullIntArg: $intArg)
+            }
+
+            query Query($intArg: Int) {
+              complicatedArgs {
+                ...outerFrag
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$intArg' of type 'Int'"
+                    " used in position expecting type 'Int!'.",
+                    "locations": [(10, 25), (7, 49)],
+                }
+            ],
+        )
+
+    def string_to_boolean():
+        assert_errors(
+            """
+            query Query($stringVar: String) {
+              complicatedArgs {
+                booleanArgField(booleanArg: $stringVar)
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$stringVar' of type 'String'"
+                    " used in position expecting type 'Boolean'.",
+                    "locations": [(2, 25), (4, 45)],
+                }
+            ],
+        )
+
+    def string_to_array_of_string():
+        assert_errors(
+            """
+            query Query($stringVar: String) {
+              complicatedArgs {
+                stringListArgField(stringListArg: $stringVar)
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$stringVar' of type 'String'"
+                    " used in position expecting type '[String]'.",
+                    "locations": [(2, 25), (4, 51)],
+                }
+            ],
+        )
+
+    def boolean_to_non_null_boolean_in_directive():
+        assert_errors(
+            """
+            query Query($boolVar: Boolean) {
+              dog @include(if: $boolVar)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$boolVar' of type 'Boolean'"
+                    " used in position expecting type 'Boolean!'.",
+                    "locations": [(2, 25), (3, 32)],
+                }
+            ],
+        )
+
+    def string_to_non_null_boolean_in_directive():
+        assert_errors(
+            """
+            query Query($stringVar: String) {
+              dog @include(if: $stringVar)
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$stringVar' of type 'String'"
+                    " used in position expecting type 'Boolean!'.",
+                    "locations": [(2, 25), (3, 32)],
+                }
+            ],
+        )
+
+    def array_of_string_to_array_of_non_null_string():
+        assert_errors(
+            """
+            query Query($stringListVar: [String])
+            {
+              complicatedArgs {
+                stringListNonNullArgField(stringListNonNullArg: $stringListVar)
+              }
+            }
+            """,
+            [
+                {
+                    "message": "Variable '$stringListVar' of type '[String]'"
+                    " used in position expecting type '[String!]'.",
+                    "locations": [(2, 25), (5, 65)],
+                }
+            ],
+        )
+
+    def describe_allows_optional_nullable_variables_with_default_values():
+        def int_to_non_null_int_fails_when_var_provides_null_default_value():
+            assert_errors(
+                """
+                query Query($intVar: Int = null) {
+                  complicatedArgs {
+                    nonNullIntArgField(nonNullIntArg: $intVar)
+                  }
+                }
+                """,
+                [
+                    {
+                        "message": "Variable '$intVar' of type 'Int'"
+                        " used in position expecting type 'Int!'.",
+                        "locations": [(2, 29), (4, 55)],
+                    }
+                ],
+            )
+
+    def int_to_non_null_int_when_var_provides_non_null_default_value():
+        assert_valid(
+            """
+            query Query($intVar: Int = 1) {
+              complicatedArgs {
+                nonNullIntArgField(nonNullIntArg: $intVar)
+              }
+            }
+            """
+        )
+
+    def int_to_non_null_int_when_optional_arg_provides_default_value():
+        assert_valid(
+            """
+            query Query($intVar: Int) {
+              complicatedArgs {
+                nonNullFieldWithDefault(nonNullIntArg: $intVar)
+              }
+            }
+            """
+        )
+
+    def bool_to_non_null_bool_in_directive_with_default_value_with_option():
+        assert_valid(
+            """
+            query Query($boolVar: Boolean = false) {
+              dog @include(if: $boolVar)
+            }
+            """
+        )
diff --git a/tests_py35/__init__.py b/tests_py35/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests_py35/core_execution/__init__.py b/tests_py35/core_execution/__init__.py
deleted file mode 100644
index 8d60ccb..0000000
--- a/tests_py35/core_execution/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-__author__ = "jake"
diff --git a/tests_py35/core_execution/test_asyncio_executor.py b/tests_py35/core_execution/test_asyncio_executor.py
deleted file mode 100644
index 8d5334d..0000000
--- a/tests_py35/core_execution/test_asyncio_executor.py
+++ /dev/null
@@ -1,106 +0,0 @@
-# flake8: noqa
-import pytest
-import asyncio
-import functools
-from graphql.error import format_error
-from graphql.execution import execute
-from graphql.language.parser import parse
-from graphql.execution.executors.asyncio import AsyncioExecutor
-from graphql.type import GraphQLSchema, GraphQLObjectType, GraphQLField, GraphQLString
-
-
-def test_asyncio_py35_executor():
-    ast = parse("query Example { a, b, c }")
-
-    async def resolver(context, *_):
-        await asyncio.sleep(0.001)
-        return "hey"
-
-    async def resolver_2(context, *_):
-        await asyncio.sleep(0.003)
-        return "hey2"
-
-    def resolver_3(context, *_):
-        return "hey3"
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "a": GraphQLField(GraphQLString, resolver=resolver),
-            "b": GraphQLField(GraphQLString, resolver=resolver_2),
-            "c": GraphQLField(GraphQLString, resolver=resolver_3),
-        },
-    )
-
-    result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor())
-    assert not result.errors
-    assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}
-
-
-def test_asyncio_py35_executor_return_promise():
-    ast = parse("query Example { a, b, c }")
-
-    async def resolver(context, *_):
-        await asyncio.sleep(0.001)
-        return "hey"
-
-    async def resolver_2(context, *_):
-        await asyncio.sleep(0.003)
-        return "hey2"
-
-    def resolver_3(context, *_):
-        return "hey3"
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "a": GraphQLField(GraphQLString, resolver=resolver),
-            "b": GraphQLField(GraphQLString, resolver=resolver_2),
-            "c": GraphQLField(GraphQLString, resolver=resolver_3),
-        },
-    )
-
-    loop = asyncio.get_event_loop()
-
-    async def do_exec():
-        result = await execute(
-            GraphQLSchema(Type),
-            ast,
-            executor=AsyncioExecutor(loop),
-            return_promise=True,
-        )
-        assert not result.errors
-        assert result.data == {"a": "hey", "b": "hey2", "c": "hey3"}
-
-    loop.run_until_complete(do_exec())
-
-
-def test_asyncio_py35_executor_with_error():
-    ast = parse("query Example { a, b }")
-
-    async def resolver(context, *_):
-        await asyncio.sleep(0.001)
-        return "hey"
-
-    async def resolver_2(context, *_):
-        await asyncio.sleep(0.003)
-        raise Exception("resolver_2 failed!")
-
-    Type = GraphQLObjectType(
-        "Type",
-        {
-            "a": GraphQLField(GraphQLString, resolver=resolver),
-            "b": GraphQLField(GraphQLString, resolver=resolver_2),
-        },
-    )
-
-    result = execute(GraphQLSchema(Type), ast, executor=AsyncioExecutor())
-    formatted_errors = list(map(format_error, result.errors))
-    assert formatted_errors == [
-        {
-            "locations": [{"line": 1, "column": 20}],
-            "path": ["b"],
-            "message": "resolver_2 failed!",
-        }
-    ]
-    assert result.data == {"a": "hey", "b": None}
diff --git a/tox.ini b/tox.ini
index 91c49c2..39f1e6b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,40 +1,50 @@
 [tox]
-envlist = py27,py34,py35,py36,py37,pre-commit,pypy,mypy,docs
+envlist = py{36,37,38}, black, flake8, mypy, docs, manifest
+isolated_build = true
 
-[testenv]
-deps =
-    pytest>=3.3,<4.0
-    gevent>=1.1
-    promise>=2.0
-    six>=1.10.0
-    pytest-mock
-    pytest-benchmark
-commands =
-    py{27,34,py}: py.test graphql tests {posargs}
-    py{35,36,37}: py.test graphql tests tests_py35 {posargs}
+[testenv:black]
+basepython = python3.8
+deps = black==19.10b0
+commands  =
+    black src tests setup.py --check
 
-[testenv:pre-commit]
-basepython=python3.6
-deps =
-    pre-commit>0.12.0
-setenv =
-    LC_CTYPE=en_US.UTF-8
+[testenv:flake8]
+basepython = python3.8
+deps = flake8>=3.8,<4
 commands =
-    pre-commit {posargs:run --all-files}
+    flake8 src tests setup.py
 
 [testenv:mypy]
-basepython=python3.6
-deps =
-    mypy
+basepython = python3.8
+deps = mypy==0.782
 commands =
-    mypy graphql --ignore-missing-imports
+    mypy src tests
 
 [testenv:docs]
-changedir = docs
-deps = sphinx
-commands = sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
+basepython = python3.8
+deps =
+    sphinx>=2.4,<3
+    sphinx_rtd_theme>=0.4,<1
+commands =
+    sphinx-build -b html -nEW docs docs/_build/html
+
+[testenv:manifest]
+basepython = python3.8
+deps = check-manifest==0.40
+commands =
+    check-manifest -v
 
-[flake8]
-# Must match black formatter default line length
-max-line-length = 88
-ignore = E203,E501,W503,W504
+[testenv]
+whitelist_externals = poetry
+setenv =
+    PYTHONPATH = {toxinidir}
+deps =
+    pytest>=5.4,<5.5
+    pytest-asyncio>=0.14,<1
+    pytest-benchmark>=3.2,<4
+    pytest-cov>=2.10,<3
+    pytest-describe>=1,<2
+    pytest-timeout>=1.4,<2
+commands =
+    poetry install
+    poetry run pytest tests {posargs: --cov-report=term-missing --cov=graphql --cov=tests --cov-fail-under=100}